1 /* 2 * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test TestMaxMinHeapFreeRatioFlags 26 * @key gc 27 * @summary Verify that heap size changes according to max and min heap free ratios. 28 * @requires vm.gc != "Z" & vm.gc != "Shenandoah" 29 * @library /test/lib 30 * @modules java.base/jdk.internal.misc 31 * java.management 32 * @run driver/timeout=240 TestMaxMinHeapFreeRatioFlags 33 */ 34 35 import java.util.LinkedList; 36 import java.util.Arrays; 37 import java.util.Collections; 38 import jdk.test.lib.process.OutputAnalyzer; 39 import jdk.test.lib.process.ProcessTools; 40 import jdk.test.lib.Utils; 41 import jdk.internal.misc.Unsafe; 42 43 public class TestMaxMinHeapFreeRatioFlags { 44 45 public static final long M = 1024 * 1024; 46 public static final long MAX_HEAP_SIZE = 200 * M; 47 public static final long HEAP_SIZE = 10 * M; 48 public static final long MAX_NEW_SIZE = 20 * M; 49 public static final long NEW_SIZE = 5 * M; 50 51 public static void main(String args[]) throws Exception { 52 LinkedList<String> options = new LinkedList<>( 53 Arrays.asList(Utils.getFilteredTestJavaOpts("-XX:[^ ]*HeapFreeRatio","-XX:\\+ExplicitGCInvokesConcurrent")) 54 ); 55 56 negativeTest(20, false, 10, true, options); 57 negativeTest(100, true, 0, false, options); 58 negativeTest(101, false, 50, false, options); 59 negativeTest(49, true, 102, true, options); 60 negativeTest(-1, false, 50, false, options); 61 negativeTest(50, true, -1, true, options); 62 63 positiveTest(10, false, 90, false, true, options); 64 positiveTest(10, true, 80, false, true, options); 65 positiveTest(20, false, 70, true, true, options); 66 positiveTest(25, true, 65, true, true, options); 67 positiveTest(40, false, 50, false, true, options); 68 } 69 70 /** 71 * Verify that heap size will be changed to conform 72 * min and max heap free ratios. 73 * 74 * @param minRatio value of MinHeapFreeRatio option 75 * @param useXminf used Xminf option instead of MinHeapFreeRatio 76 * @param maxRatio value of MaxHeapFreeRatio option 77 * @param useXmaxf used Xmaxf option instead of MaxHeapFreeRatio 78 * @param options additional options for JVM 79 */ 80 public static void positiveTest(int minRatio, boolean useXminf, 81 int maxRatio, boolean useXmaxf, boolean shrinkHeapInSteps, 82 LinkedList<String> options) throws Exception { 83 84 LinkedList<String> vmOptions = new LinkedList<>(options); 85 Collections.addAll(vmOptions, 86 (useXminf ? "-Xminf" + minRatio / 100.0 : "-XX:MinHeapFreeRatio=" + minRatio), 87 (useXmaxf ? "-Xmaxf" + maxRatio / 100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio), 88 "-Xmx" + MAX_HEAP_SIZE, 89 "-Xms" + HEAP_SIZE, 90 "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", 91 "-XX:NewSize=" + NEW_SIZE, 92 "-XX:MaxNewSize=" + MAX_NEW_SIZE, 93 "-XX:" + (shrinkHeapInSteps ? '+' : '-') + "ShrinkHeapInSteps", 94 RatioVerifier.class.getName(), 95 Integer.toString(minRatio), 96 Integer.toString(maxRatio), 97 Boolean.toString(shrinkHeapInSteps) 98 ); 99 100 ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()])); 101 OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start()); 102 analyzer.shouldHaveExitValue(0); 103 } 104 105 /** 106 * Verify that VM will fail to start with specified ratios. 107 * 108 * @param minRatio value of MinHeapFreeRatio option 109 * @param useXminf used Xminf option instead of MinHeapFreeRatio 110 * @param maxRatio value of MaxHeapFreeRatio option 111 * @param useXmaxf used Xmaxf option instead of MaxHeapFreeRatio 112 * @param options additional options for JVM 113 */ 114 public static void negativeTest(int minRatio, boolean useXminf, 115 int maxRatio, boolean useXmaxf, 116 LinkedList<String> options) throws Exception { 117 118 LinkedList<String> vmOptions = new LinkedList<>(options); 119 Collections.addAll(vmOptions, 120 (useXminf ? "-Xminf" + minRatio / 100.0 : "-XX:MinHeapFreeRatio=" + minRatio), 121 (useXmaxf ? "-Xmaxf" + maxRatio / 100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio), 122 "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", 123 "-version" 124 ); 125 ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()])); 126 OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start()); 127 analyzer.shouldHaveExitValue(1); 128 analyzer.shouldContain("Error: Could not create the Java Virtual Machine."); 129 } 130 131 /** 132 * RatioVerifier will be executed in the tested VM. 133 * It will check that real heap usage after collection lies between MinHeapFreeRatio and MaxHeapFreeRatio. 134 */ 135 public static class RatioVerifier { 136 137 private static final Unsafe unsafe = Unsafe.getUnsafe(); 138 139 // Size of byte array that will be allocated 140 public static final int CHUNK_SIZE = 1024; 141 // Length of byte array, that will be added to "garbage" list. 142 public static final int ARRAY_LENGTH = CHUNK_SIZE - Unsafe.ARRAY_BYTE_BASE_OFFSET; 143 // Amount of tries to force heap shrinking/expansion using GC 144 public static final int GC_TRIES = 10; 145 146 // Value that will be added/substracted from expected min/max heap free ratio 147 // during memory allocation to make sure that specified limit will be exceeded. 148 public static final double OVERLOAD = 0.05; 149 // Acceptable heap free ratio limit exceedance: verification will fail if 150 // actual ratio is lower than expected min heap free ratio - VARIANCE or 151 // higher than expected max heap free ratio + VARIANCE. 152 public static final double VARIANCE = 0.025; 153 154 public static LinkedList<Object> garbage = new LinkedList<>(); 155 156 public static void main(String args[]) throws Exception { 157 if (args.length != 3) { 158 throw new IllegalArgumentException("Expected 3 args: <minRatio> <maxRatio> <shrinkHeapInSteps>"); 159 } 160 if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.PSOld || 161 GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) { 162 System.out.println("Test is not applicable to parallel full GCs"); 163 return; 164 } 165 166 double minRatio = Integer.valueOf(args[0]) / 100.0; 167 double maxRatio = Integer.valueOf(args[1]) / 100.0; 168 boolean shrinkHeapInSteps = Boolean.valueOf(args[2]); 169 170 long maxHeapSize = getMax(); 171 int gcTries = (shrinkHeapInSteps ? GC_TRIES : 1); 172 173 // Initial checks. This also links up everything in these helper methods, 174 // in case it brings more garbage. 175 forceGC(gcTries); 176 verifyRatio(minRatio, maxRatio); 177 178 // commit 0.5 of total heap size to have enough space 179 // to both shink and expand 180 while (getCommitted() < maxHeapSize / 2) { 181 garbage.add(new byte[ARRAY_LENGTH]); 182 } 183 184 forceGC(gcTries); 185 // Verify that current heap free ratio lies between specified limits 186 verifyRatio(minRatio, maxRatio); 187 188 // Estimate how much memory we have to allocate to force expansion 189 long memoryToFill = (long) (getCommitted() * (1 - minRatio + OVERLOAD)) 190 - getUsed(); 191 192 long previouslyCommitted = getCommitted(); 193 194 while (memoryToFill > 0) { 195 garbage.add(new byte[CHUNK_SIZE]); 196 memoryToFill -= CHUNK_SIZE; 197 } 198 199 forceGC(gcTries); 200 // Verify that after memory allocation heap free ratio is still conforming specified limits 201 verifyRatio(minRatio, maxRatio); 202 // Verify that heap was actually expanded 203 if (previouslyCommitted >= getCommitted()) { 204 throw new RuntimeException("Heap was not expanded."); 205 } 206 207 // Estimate how much memory we have to free to force shrinking 208 long memoryToFree = getUsed() 209 - (long) (getCommitted() * (1 - maxRatio - OVERLOAD)); 210 211 previouslyCommitted = getCommitted(); 212 213 while (memoryToFree > 0 && garbage.size() > 0) { 214 garbage.remove(garbage.size() - 1); 215 memoryToFree -= CHUNK_SIZE; 216 } 217 218 forceGC(gcTries); 219 // Verify that heap free ratio is still conforming specified limits 220 verifyRatio(minRatio, maxRatio); 221 // Verify that heap was actually shrinked 222 if (previouslyCommitted <= getCommitted()) { 223 throw new RuntimeException("Heap was not shrinked."); 224 } 225 } 226 227 public static void forceGC(int gcTries) { 228 for (int i = 0; i < gcTries; i++) { 229 System.gc(); 230 try { 231 Thread.sleep(10); 232 } catch (InterruptedException ie) { 233 } 234 } 235 } 236 237 /** 238 * Verify that heap free ratio is conforming specified limits. 239 * Actual heap free ratio may be very close to one of specified limits, 240 * but exceed for more then VARIANCE. 241 * Verification will also pass if actual ratio is not conforming limits, 242 * but it is not possible to shrink/expand heap. 243 */ 244 public static void verifyRatio(double minRatio, double maxRatio) { 245 double ratio = getHeapFreeRatio(); 246 System.out.println(minRatio + " " + ratio + " " + maxRatio); 247 if (minRatio - ratio > VARIANCE 248 && getCommitted() < getMax()) { 249 throw new RuntimeException("Current heap free ratio is lower than " 250 + "MinHeapFreeRatio (" + ratio + " vs " + minRatio + ")."); 251 } 252 if (ratio - maxRatio > VARIANCE 253 && getUsed() > getInit()) { 254 throw new RuntimeException("Current heap free ratio is higher than " 255 + "MaxHeapFreeRatio (" + ratio + " vs " + maxRatio + ")."); 256 } 257 } 258 259 /* 260 * Obtain information about heap size. 261 * 262 * For G1 information summed up for all type of regions, 263 * because tested options affect overall heap sizing. 264 * 265 * For all other GCs return information only for old gen. 266 */ 267 public static long getMax() { 268 return HeapRegionUsageTool.getOldUsage().getMax(); 269 } 270 271 public static long getInit() { 272 if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) { 273 return HeapRegionUsageTool.getEdenUsage().getInit() 274 + HeapRegionUsageTool.getSurvivorUsage().getInit() 275 + HeapRegionUsageTool.getOldUsage().getInit(); 276 } else { 277 return HeapRegionUsageTool.getOldUsage().getInit(); 278 } 279 } 280 281 public static long getUsed() { 282 if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) { 283 return HeapRegionUsageTool.getEdenUsage().getUsed() 284 + HeapRegionUsageTool.getSurvivorUsage().getUsed() 285 + HeapRegionUsageTool.getOldUsage().getUsed(); 286 } else { 287 return HeapRegionUsageTool.getOldUsage().getUsed(); 288 } 289 } 290 291 public static long getCommitted() { 292 if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) { 293 return HeapRegionUsageTool.getEdenUsage().getCommitted() 294 + HeapRegionUsageTool.getSurvivorUsage().getCommitted() 295 + HeapRegionUsageTool.getOldUsage().getCommitted(); 296 } else { 297 return HeapRegionUsageTool.getOldUsage().getCommitted(); 298 } 299 } 300 301 public static long getFree() { 302 return getCommitted() - getUsed(); 303 } 304 305 public static double getHeapFreeRatio() { 306 return getFree() / (double) getCommitted(); 307 } 308 } 309 }