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