1 /* 2 * Copyright (c) 2016, 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 * @library /testlibrary 29 * @modules java.base/jdk.internal.misc 30 * java.management 31 * @build TestMaxMinHeapFreeRatioFlags 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.OutputAnalyzer; 39 import jdk.test.lib.ProcessTools; 40 import jdk.test.lib.Utils; 41 import jdk.test.lib.HeapRegionUsageTool; 42 import jdk.internal.misc.Unsafe; 43 44 public class TestMaxMinHeapFreeRatioFlags { 45 46 public static final long M = 1024 * 1024; 47 public static final long MAX_HEAP_SIZE = 200 * M; 48 public static final long HEAP_SIZE = 10 * M; 49 public static final long MAX_NEW_SIZE = 20 * M; 50 public static final long NEW_SIZE = 5 * M; 51 52 public static void main(String args[]) throws Exception { 53 LinkedList<String> options = new LinkedList<>( 54 Arrays.asList(Utils.getFilteredTestJavaOpts("-XX:[^ ]*HeapFreeRatio","-XX:\\+ExplicitGCInvokesConcurrent")) 55 ); 56 57 negativeTest(20, false, 10, true, options); 58 negativeTest(100, true, 0, false, options); 59 negativeTest(101, false, 50, false, options); 60 negativeTest(49, true, 102, true, options); 61 negativeTest(-1, false, 50, false, options); 62 negativeTest(50, true, -1, true, options); 63 64 positiveTest(10, false, 90, false, true, options); 65 positiveTest(10, true, 80, false, true, options); 66 positiveTest(20, false, 70, true, true, options); 67 positiveTest(25, true, 65, true, true, options); 68 positiveTest(40, false, 50, false, true, options); 69 } 70 71 /** 72 * Verify that heap size will be changed to conform 73 * min and max heap free ratios. 74 * 75 * @param minRatio value of MinHeapFreeRatio option 76 * @param useXminf used Xminf option instead of MinHeapFreeRatio 77 * @param maxRatio value of MaxHeapFreeRatio option 78 * @param useXmaxf used Xmaxf option instead of MaxHeapFreeRatio 79 * @param options additional options for JVM 80 */ 81 public static void positiveTest(int minRatio, boolean useXminf, 82 int maxRatio, boolean useXmaxf, boolean shrinkHeapInSteps, 83 LinkedList<String> options) throws Exception { 84 85 LinkedList<String> vmOptions = new LinkedList<>(options); 86 Collections.addAll(vmOptions, 87 (useXminf ? "-Xminf" + minRatio / 100.0 : "-XX:MinHeapFreeRatio=" + minRatio), 88 (useXmaxf ? "-Xmaxf" + maxRatio / 100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio), 89 "-Xmx" + MAX_HEAP_SIZE, 90 "-Xms" + HEAP_SIZE, 91 "-XaddExports:java.base/jdk.internal.misc=ALL-UNNAMED", 92 "-XX:NewSize=" + NEW_SIZE, 93 "-XX:MaxNewSize=" + MAX_NEW_SIZE, 94 "-XX:" + (shrinkHeapInSteps ? '+' : '-') + "ShrinkHeapInSteps", 95 RatioVerifier.class.getName(), 96 Integer.toString(minRatio), 97 Integer.toString(maxRatio), 98 Boolean.toString(shrinkHeapInSteps) 99 ); 100 101 ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()])); 102 OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start()); 103 analyzer.shouldHaveExitValue(0); 104 } 105 106 /** 107 * Verify that VM will fail to start with specified ratios. 108 * 109 * @param minRatio value of MinHeapFreeRatio option 110 * @param useXminf used Xminf option instead of MinHeapFreeRatio 111 * @param maxRatio value of MaxHeapFreeRatio option 112 * @param useXmaxf used Xmaxf option instead of MaxHeapFreeRatio 113 * @param options additional options for JVM 114 */ 115 public static void negativeTest(int minRatio, boolean useXminf, 116 int maxRatio, boolean useXmaxf, 117 LinkedList<String> options) throws Exception { 118 119 LinkedList<String> vmOptions = new LinkedList<>(options); 120 Collections.addAll(vmOptions, 121 (useXminf ? "-Xminf" + minRatio / 100.0 : "-XX:MinHeapFreeRatio=" + minRatio), 122 (useXmaxf ? "-Xmaxf" + maxRatio / 100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio), 123 "-XaddExports:java.base/jdk.internal.misc=ALL-UNNAMED", 124 "-version" 125 ); 126 ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()])); 127 OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start()); 128 analyzer.shouldHaveExitValue(1); 129 analyzer.shouldContain("Error: Could not create the Java Virtual Machine."); 130 } 131 132 /** 133 * RatioVerifier will be executed in the tested VM. 134 * It will check that real heap usage after collection lies between MinHeapFreeRatio and MaxHeapFreeRatio. 135 */ 136 public static class RatioVerifier { 137 138 private static final Unsafe unsafe = Utils.getUnsafe(); 139 140 // Size of byte array that will be allocated 141 public static final int CHUNK_SIZE = 1024; 142 // Length of byte array, that will be added to "garbage" list. 143 public static final int ARRAY_LENGTH = CHUNK_SIZE - Unsafe.ARRAY_BYTE_BASE_OFFSET; 144 // Amount of tries to force heap shrinking/expansion using GC 145 public static final int GC_TRIES = 10; 146 147 // Value that will be added/substracted from expected min/max heap free ratio 148 // during memory allocation to make sure that specified limit will be exceeded. 149 public static final double OVERLOAD = 0.05; 150 // Acceptable heap free ratio limit exceedance: verification will fail if 151 // actual ratio is lower than expected min heap free ratio - VARIANCE or 152 // higher than expected max heap free ratio + VARIANCE. 153 public static final double VARIANCE = 0.025; 154 155 public static LinkedList<Object> garbage = new LinkedList<>(); 156 157 public static void main(String args[]) throws Exception { 158 if (args.length != 3) { 159 throw new IllegalArgumentException("Expected 3 args: <minRatio> <maxRatio> <shrinkHeapInSteps>"); 160 } 161 if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.PSOld) { 162 System.out.println("Test is not applicable to parallel GC"); 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 }