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