1 /* 2 * Copyright (c) 2015, 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 TestNewSizeFlags 28 * @bug 8025166 29 * @summary Verify that young gen size conforms values specified by NewSize, MaxNewSize and Xmn options 30 * @requires vm.gc != "Z" & vm.gc != "Shenandoah" 31 * @library /test/lib 32 * @library / 33 * @modules java.base/jdk.internal.misc 34 * java.management 35 * @build sun.hotspot.WhiteBox 36 * @run driver ClassFileInstaller sun.hotspot.WhiteBox 37 * @run driver/timeout=240 gc.arguments.TestNewSizeFlags 38 */ 39 40 import java.io.IOException; 41 import java.lang.management.MemoryUsage; 42 import java.util.Arrays; 43 import java.util.Collections; 44 import java.util.LinkedList; 45 import jdk.test.lib.process.OutputAnalyzer; 46 import jdk.test.lib.process.ProcessTools; 47 import jdk.test.lib.Utils; 48 import sun.hotspot.WhiteBox; 49 50 public class TestNewSizeFlags { 51 52 public static final long M = 1024 * 1024; 53 main(String args[])54 public static void main(String args[]) throws Exception { 55 LinkedList<String> options = new LinkedList<>( 56 Arrays.asList(Utils.getFilteredTestJavaOpts("(-Xm[nsx][^ ]+)|" 57 + "(-XX:(Max)?((New)|" 58 + "(Heap))((Size)|" 59 + "(Ratio))=[^ ]+)")) 60 ); 61 62 // Test NewSize and MaxNewSize 63 testNewSizeFlags(20 * M, 10 * M, 30 * M, 40 * M, options, false); 64 testNewSizeFlags(10 * M, 20 * M, 30 * M, 80 * M, options, false); 65 testNewSizeFlags(-1, 20 * M, 30 * M, 40 * M, options, false); 66 testNewSizeFlags(10 * M, -1, 30 * M, 40 * M, options, false); 67 testNewSizeFlags(20 * M, 20 * M, 30 * M, 40 * M, options, false); 68 testNewSizeFlags(20 * M, 30 * M, 40 * M, 50 * M, options, false); 69 testNewSizeFlags(30 * M, 100 * M, 150 * M, 200 * M, options, false); 70 testNewSizeFlags(20 * M, 30 * M, 128 * M, 128 * M, options, false); 71 72 // Test -Xmn 73 testXmnFlags(0, 30 * M, 40 * M, options, true); 74 testXmnFlags(20 * M, 30 * M, 40 * M, options, false); 75 testXmnFlags(50 * M, 70 * M, 100 * M, options, false); 76 } 77 78 /** 79 * Verify that NewSize and MaxNewSize flags affect young gen size. 80 * 81 * @param newSize value of NewSize option, omitted if negative 82 * @param maxNewSize value of MaxNewSize option, omitted if negative 83 * @param heapSize value of HeapSize option 84 * @param maxHeapSize value of MaxHeapSize option 85 * @param options additional options for JVM 86 * @param failureExpected true if JVM should fail with passed heap size options 87 */ testNewSizeFlags(long newSize, long maxNewSize, long heapSize, long maxHeapSize, LinkedList<String> options, boolean failureExpected)88 public static void testNewSizeFlags(long newSize, long maxNewSize, 89 long heapSize, long maxHeapSize, 90 LinkedList<String> options, 91 boolean failureExpected) throws Exception { 92 long expectedNewSize = newSize; 93 long expectedMaxNewSize = (maxNewSize >= 0 ? Math.max(maxNewSize, newSize) : maxNewSize); 94 testVMOptions(newSize, maxNewSize, 95 heapSize, maxHeapSize, 96 expectedNewSize, expectedMaxNewSize, 97 options, failureExpected); 98 } 99 100 /** 101 * Verify that -Xmn flag affect young gen size. 102 * 103 * @param mnValue value of -Xmn option 104 * @param heapSize value of HeapSize option 105 * @param maxHeapSize value of MaxHeapSize option 106 * @param options additional options for JVM 107 * @param failureExpected true if JVM should fail with passed heap size options 108 */ testXmnFlags(long mnValue, long heapSize, long maxHeapSize, LinkedList<String> options, boolean failureExpected)109 public static void testXmnFlags(long mnValue, 110 long heapSize, long maxHeapSize, 111 LinkedList<String> options, 112 boolean failureExpected) throws Exception { 113 LinkedList<String> newOptions = new LinkedList<>(options); 114 newOptions.add("-Xmn" + mnValue); 115 testVMOptions(-1, -1, 116 heapSize, maxHeapSize, 117 mnValue, mnValue, 118 newOptions, failureExpected); 119 } 120 121 /** 122 * Verify that NewSize and MaxNewSize flags affect young gen size. 123 * 124 * @param newSize value of NewSize option, omitted if negative 125 * @param maxNewSize value of MaxNewSize option, omitted if negative 126 * @param heapSize value of HeapSize option 127 * @param maxHeapSize value of MaxHeapSize option 128 * @param expectedNewSize expected initial young gen size 129 * @param expectedMaxNewSize expected max young gen size 130 * @param options additional options for JVM 131 * @param failureExpected true if JVM should fail with passed heap size options 132 */ testVMOptions(long newSize, long maxNewSize, long heapSize, long maxHeapSize, long expectedNewSize, long expectedMaxNewSize, LinkedList<String> options, boolean failureExpected)133 public static void testVMOptions(long newSize, long maxNewSize, 134 long heapSize, long maxHeapSize, 135 long expectedNewSize, long expectedMaxNewSize, 136 LinkedList<String> options, boolean failureExpected) throws Exception { 137 OutputAnalyzer analyzer = startVM(options, newSize, maxNewSize, heapSize, maxHeapSize, expectedNewSize, expectedMaxNewSize); 138 139 if (failureExpected) { 140 analyzer.shouldHaveExitValue(1); 141 analyzer.shouldMatch("(Error occurred during initialization of VM)|" 142 + "(Error: Could not create the Java Virtual Machine.)"); 143 } else { 144 analyzer.shouldHaveExitValue(0); 145 } 146 } 147 startVM(LinkedList<String> options, long newSize, long maxNewSize, long heapSize, long maxHeapSize, long expectedNewSize, long expectedMaxNewSize)148 private static OutputAnalyzer startVM(LinkedList<String> options, 149 long newSize, long maxNewSize, 150 long heapSize, long maxHeapSize, 151 long expectedNewSize, long expectedMaxNewSize) throws Exception, IOException { 152 LinkedList<String> vmOptions = new LinkedList<>(options); 153 Collections.addAll(vmOptions, 154 "-Xbootclasspath/a:.", 155 "-XX:+UnlockDiagnosticVMOptions", 156 "-XX:+WhiteBoxAPI", 157 (newSize >= 0 ? "-XX:NewSize=" + newSize : ""), 158 (maxNewSize >= 0 ? "-XX:MaxNewSize=" + maxNewSize : ""), 159 "-Xmx" + maxHeapSize, 160 "-Xms" + heapSize, 161 "-XX:GCLockerEdenExpansionPercent=0", 162 "-XX:-UseLargePages", 163 NewSizeVerifier.class.getName(), 164 Long.toString(expectedNewSize), 165 Long.toString(expectedMaxNewSize), 166 Long.toString(heapSize), 167 Long.toString(maxHeapSize) 168 ); 169 vmOptions.removeIf(String::isEmpty); 170 ProcessBuilder procBuilder = GCArguments.createJavaProcessBuilder(vmOptions); 171 OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start()); 172 return analyzer; 173 } 174 175 /** 176 * NewSizeVerifier checks that initial young gen size is equal to expected 177 * regardful to alignment and that young gen size will not be greater than 178 * expected max size. 179 * In order to verify that young gen size will not be greater then expected 180 * max size, NewSizeVerifier do some object allocation to force garbage 181 * collection and heap expansion. 182 */ 183 public static class NewSizeVerifier { 184 185 private static final WhiteBox WB = WhiteBox.getWhiteBox(); 186 private static final GCTypes.YoungGCType YOUNG_GC_TYPE = GCTypes.YoungGCType.getYoungGCType(); 187 private static final long HEAP_SPACE_ALIGNMENT = WB.getHeapSpaceAlignment(); 188 private static final long HEAP_ALIGNMENT = WB.getHeapAlignment(); 189 private static final long PS_VIRTUAL_SPACE_ALIGNMENT = 190 (YOUNG_GC_TYPE == GCTypes.YoungGCType.PSNew) ? WB.psVirtualSpaceAlignment() : 0; 191 192 public static final int ARRAY_LENGTH = 100; 193 public static final int CHUNK_SIZE = 1024; 194 public static final int MAX_ITERATIONS = 10; 195 public static byte garbage[][] = new byte[ARRAY_LENGTH][]; 196 main(String args[])197 public static void main(String args[]) throws Exception { 198 if (args.length != 4) { 199 throw new IllegalArgumentException("Expected 4 args: <expectedNewSize> <expectedMaxNewSize> <initialHeapSize> <maxHeapSize>"); 200 } 201 final long newSize = Long.valueOf(args[0]); 202 final long maxNewSize = Long.valueOf(args[1]); 203 final long initialHeapSize = Long.valueOf(args[2]); 204 final long maxHeapSize = Long.valueOf(args[3]); 205 206 // verify initial size 207 verifyNewSize(newSize, maxNewSize, initialHeapSize, maxHeapSize); 208 209 // force GC and verify that size is still correct 210 AllocationHelper allocator = new AllocationHelper(MAX_ITERATIONS, ARRAY_LENGTH, CHUNK_SIZE, () -> (verifyNewSize(newSize, maxNewSize, initialHeapSize, maxHeapSize))); 211 allocator.allocateMemoryAndVerifyNoOOME(); 212 } 213 214 /** 215 * Verify that actual young gen size conforms NewSize and MaxNewSize values. 216 */ verifyNewSize(long newSize, long maxNewSize, long initialHeapSize, long maxHeapSize)217 public static Void verifyNewSize(long newSize, long maxNewSize, 218 long initialHeapSize, long maxHeapSize) { 219 long alignedNewSize = alignGenSize(newSize); 220 long alignedMaxNewSize = alignGenSize(maxNewSize); 221 long alignedXms = alignHeapSize(initialHeapSize); 222 long alignedXmx = alignHeapSize(maxHeapSize); 223 224 MemoryUsage youngGenUsage = getYoungGenUsage(); 225 long initSize = youngGenUsage.getInit(); 226 long commitedSize = youngGenUsage.getCommitted(); 227 long maxSize = youngGenUsage.getMax(); 228 229 if (newSize != -1) { 230 if (initSize < alignedNewSize) { 231 throw new RuntimeException("initial new size < NewSize value: " 232 + initSize + " < " + alignedNewSize); 233 } 234 235 if (commitedSize < alignedNewSize) { 236 throw new RuntimeException("actual new size < NewSize value: " 237 + commitedSize + " < " + alignedNewSize); 238 } 239 240 // for G1 max new size == committed new size 241 if (YOUNG_GC_TYPE != GCTypes.YoungGCType.G1 242 && maxSize < alignedNewSize) { 243 throw new RuntimeException("max new size < NewSize value: " 244 + maxSize + " < " + alignedNewSize); 245 } 246 } 247 248 if (maxNewSize != -1) { 249 if (initSize > alignedMaxNewSize) { 250 throw new RuntimeException("initial new size > MaxNewSize value: " 251 + initSize + " > " + alignedMaxNewSize); 252 } 253 254 if (commitedSize > alignedMaxNewSize) { 255 throw new RuntimeException("actual new size > MaxNewSize value: " 256 + commitedSize + " > " + alignedMaxNewSize); 257 } 258 259 if (alignedXms != alignedXmx) { 260 if (YOUNG_GC_TYPE != GCTypes.YoungGCType.G1 261 && maxSize != alignedMaxNewSize) { 262 throw new RuntimeException("max new size != MaxNewSize value: " 263 + maxSize + " != " + alignedMaxNewSize); 264 } 265 } else { 266 if (YOUNG_GC_TYPE != GCTypes.YoungGCType.G1 267 && maxSize != alignedNewSize) { 268 throw new RuntimeException("max new size != NewSize for case InitialHeapSize == MaxHeapSize value: " 269 + maxSize + " != " + alignedNewSize + " HeapSize == " + alignedXms); 270 } 271 } 272 } 273 return null; 274 } 275 276 /** 277 * Get young gen memory usage. 278 * 279 * For G1 it is EdenUsage + SurvivorUsage, 280 * for other GCs it is EdenUsage + 2 * SurvivorUsage. 281 * For G1 max value is just LONG_MAX. 282 * For all GCs used value is 0. 283 */ getYoungGenUsage()284 private static MemoryUsage getYoungGenUsage() { 285 MemoryUsage edenUsage = HeapRegionUsageTool.getEdenUsage(); 286 MemoryUsage survivorUsage = HeapRegionUsageTool.getSurvivorUsage(); 287 long edenUsageInit = edenUsage.getInit(); 288 long edenUsageCommited = edenUsage.getCommitted(); 289 long survivorUsageInit = survivorUsage.getInit(); 290 long survivorUsageCommited = survivorUsage.getCommitted(); 291 292 if (YOUNG_GC_TYPE == GCTypes.YoungGCType.G1) { 293 return new MemoryUsage(edenUsageInit + survivorUsageInit, 0, 294 edenUsageCommited + survivorUsageCommited, Long.MAX_VALUE); 295 } else { 296 return new MemoryUsage(edenUsageInit + survivorUsageInit * 2, 0, 297 edenUsageCommited + survivorUsageCommited * 2, 298 edenUsage.getMax() + survivorUsage.getMax() * 2); 299 } 300 } 301 302 /** 303 * Align generation size regardful to used young GC. 304 */ alignGenSize(long value)305 public static long alignGenSize(long value) { 306 switch (YOUNG_GC_TYPE) { 307 case DefNew: 308 return HeapRegionUsageTool.alignDown(value, HEAP_SPACE_ALIGNMENT); 309 case PSNew: 310 return HeapRegionUsageTool.alignUp(HeapRegionUsageTool.alignDown(value, 311 HEAP_SPACE_ALIGNMENT), 312 PS_VIRTUAL_SPACE_ALIGNMENT); 313 case G1: 314 return HeapRegionUsageTool.alignUp(value, WB.g1RegionSize()); 315 default: 316 throw new RuntimeException("Unexpected young GC type"); 317 } 318 } 319 320 /** 321 * Align heap size. 322 */ alignHeapSize(long value)323 public static long alignHeapSize(long value){ 324 return HeapRegionUsageTool.alignUp(value,HEAP_ALIGNMENT); 325 } 326 } 327 } 328