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