1 /* 2 * Copyright (c) 2015, 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 /testlibrary /test/lib 30 * @modules java.base/sun.misc 31 * java.management 32 * @build TestNewSizeFlags 33 * @run main ClassFileInstaller sun.hotspot.WhiteBox 34 * @run driver/timeout=240 TestNewSizeFlags 35 */ 36 37 import jdk.test.lib.AllocationHelper; 38 import java.io.IOException; 39 import java.lang.management.MemoryUsage; 40 import java.util.Arrays; 41 import java.util.Collections; 42 import java.util.LinkedList; 43 import jdk.test.lib.HeapRegionUsageTool; 44 import jdk.test.lib.OutputAnalyzer; 45 import jdk.test.lib.ProcessTools; 46 import jdk.test.lib.Utils; 47 import sun.hotspot.WhiteBox; 48 49 public class TestNewSizeFlags { 50 51 public static final long M = 1024 * 1024; 52 53 public static void main(String args[]) throws Exception { 54 LinkedList<String> options = new LinkedList<>( 55 Arrays.asList(Utils.getFilteredTestJavaOpts("(-Xm[nsx][^ ]+)|" 56 + "(-XX:(Max)?((New)|" 57 + "(Heap))((Size)|" 58 + "(Ratio))=[^ ]+)")) 59 ); 60 61 // Test NewSize and MaxNewSize 62 testNewSizeFlags(20 * M, 10 * M, 30 * M, 40 * M, options, false); 63 testNewSizeFlags(10 * M, 20 * M, 30 * M, 40 * M, options, false); 64 testNewSizeFlags(-1, 20 * M, 30 * M, 40 * M, options, false); 65 testNewSizeFlags(10 * M, -1, 30 * M, 40 * M, options, false); 66 testNewSizeFlags(20 * M, 20 * M, 30 * M, 40 * M, options, false); 67 testNewSizeFlags(20 * M, 30 * M, 40 * M, 50 * M, options, false); 68 testNewSizeFlags(30 * M, 100 * M, 150 * M, 200 * M, options, false); 69 testNewSizeFlags(0, -1, 30 * M, 40 * M, options, false); 70 71 // Test -Xmn 72 testXmnFlags(0, 30 * M, 40 * M, options, true); 73 testXmnFlags(20 * M, 30 * M, 40 * M, options, false); 74 testXmnFlags(50 * M, 70 * M, 100 * M, options, false); 75 } 76 77 /** 78 * Verify that NewSize and MaxNewSize flags affect young gen size. 79 * 80 * @param newSize value of NewSize option, omitted if negative 81 * @param maxNewSize value of MaxNewSize option, omitted if negative 82 * @param heapSize value of HeapSize option 83 * @param maxHeapSize value of MaxHeapSize option 84 * @param options additional options for JVM 85 * @param failureExpected true if JVM should fail with passed heap size options 86 */ 87 public static void testNewSizeFlags(long newSize, long maxNewSize, 88 long heapSize, long maxHeapSize, 89 LinkedList<String> options, 90 boolean failureExpected) throws Exception { 91 testVMOptions(newSize, maxNewSize, 92 heapSize, maxHeapSize, 93 newSize, (maxNewSize >= 0 ? Math.max(maxNewSize, newSize) : maxNewSize), 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 ); 164 vmOptions.removeIf(String::isEmpty); 165 ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()])); 166 OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start()); 167 return analyzer; 168 } 169 170 /** 171 * NewSizeVerifier checks that initial young gen size is equal to expected 172 * regardful to alignment and that young gen size will not be greater than 173 * expected max size. 174 * In order to verify that young gen size will not be greater then expected 175 * max size, NewSizeVerifier do some object allocation to force garbage 176 * collection and heap expansion. 177 */ 178 public static class NewSizeVerifier { 179 180 static WhiteBox wb = WhiteBox.getWhiteBox(); 181 182 public static final int ARRAY_LENGTH = 100; 183 public static final int CHUNK_SIZE = 1024; 184 public static final int MAX_ITERATIONS = 10; 185 public static byte garbage[][] = new byte[ARRAY_LENGTH][]; 186 187 public static void main(String args[]) throws Exception { 188 if (args.length != 2) { 189 throw new IllegalArgumentException("Expected 2 args: <expectedNewSize> <expectedMaxNewSize>"); 190 } 191 final long newSize = Long.valueOf(args[0]); 192 final long maxNewSize = Long.valueOf(args[1]); 193 194 // verify initial size 195 verifyNewSize(newSize, maxNewSize); 196 197 // force GC and verify that size is still correct 198 AllocationHelper allocator = new AllocationHelper(MAX_ITERATIONS, ARRAY_LENGTH, CHUNK_SIZE, () -> (verifyNewSize(newSize, maxNewSize))); 199 allocator.allocateMemoryAndVerifyNoOOME(); 200 } 201 202 /** 203 * Verify that actual young gen size conforms NewSize and MaxNewSize values. 204 */ 205 public static Void verifyNewSize(long newSize, long maxNewSize) { 206 long alignedNewSize = alignNewSize(newSize); 207 long alignedMaxNewSize = alignNewSize(maxNewSize); 208 209 MemoryUsage youngGenUsage = getYoungGenUsage(); 210 211 if (newSize != -1) { 212 if (youngGenUsage.getInit() < alignedNewSize) { 213 throw new RuntimeException("initial new size < NewSize value: " 214 + youngGenUsage.getInit() + " < " + alignedNewSize); 215 } 216 217 if (youngGenUsage.getCommitted() < alignedNewSize) { 218 throw new RuntimeException("actual new size < NewSize value: " 219 + youngGenUsage.getCommitted() + " < " + alignedNewSize); 220 } 221 222 // for G1 max new size == committed new size 223 if (GCTypes.YoungGCType.getYoungGCType() != GCTypes.YoungGCType.G1 224 && youngGenUsage.getMax() < alignedNewSize) { 225 throw new RuntimeException("max new size < NewSize value: " 226 + youngGenUsage.getMax() + " < " + alignedNewSize); 227 } 228 } 229 230 if (maxNewSize != -1) { 231 if (youngGenUsage.getInit() > alignedMaxNewSize) { 232 throw new RuntimeException("initial new size > MaxNewSize value: " 233 + youngGenUsage.getInit() + " > " + alignedMaxNewSize); 234 } 235 236 if (youngGenUsage.getCommitted() > alignedMaxNewSize) { 237 throw new RuntimeException("actual new size > MaxNewSize value: " 238 + youngGenUsage.getCommitted() + " > " + alignedMaxNewSize); 239 } 240 241 if (GCTypes.YoungGCType.getYoungGCType() != GCTypes.YoungGCType.G1 242 && youngGenUsage.getMax() != alignedMaxNewSize) { 243 throw new RuntimeException("max new size != MaxNewSize value: " 244 + youngGenUsage.getMax() + " != " + alignedMaxNewSize); 245 } 246 } 247 return null; 248 } 249 250 /** 251 * Get young gen memory usage. 252 * 253 * For G1 it is EdenUsage + SurvivorUsage, 254 * for other GCs it is EdenUsage + 2 * SurvivorUsage. 255 * For G1 max value is just LONG_MAX. 256 * For all GCs used value is 0. 257 */ 258 private static MemoryUsage getYoungGenUsage() { 259 if (GCTypes.YoungGCType.getYoungGCType() == GCTypes.YoungGCType.G1) { 260 return new MemoryUsage(HeapRegionUsageTool.getEdenUsage().getInit() 261 + HeapRegionUsageTool.getSurvivorUsage().getInit(), 262 0, 263 HeapRegionUsageTool.getEdenUsage().getCommitted() 264 + HeapRegionUsageTool.getSurvivorUsage().getCommitted(), 265 Long.MAX_VALUE); 266 } else { 267 return new MemoryUsage(HeapRegionUsageTool.getEdenUsage().getInit() 268 + HeapRegionUsageTool.getSurvivorUsage().getInit() * 2, 269 0, 270 HeapRegionUsageTool.getEdenUsage().getCommitted() 271 + HeapRegionUsageTool.getSurvivorUsage().getCommitted() * 2, 272 HeapRegionUsageTool.getEdenUsage().getMax() 273 + HeapRegionUsageTool.getSurvivorUsage().getMax() * 2); 274 } 275 } 276 277 /** 278 * Align value regardful to used young GC. 279 */ 280 public static long alignNewSize(long value) { 281 switch (GCTypes.YoungGCType.getYoungGCType()) { 282 case DefNew: 283 case ParNew: 284 return HeapRegionUsageTool.alignDown(value, wb.getHeapSpaceAlignment()); 285 case PSNew: 286 return HeapRegionUsageTool.alignUp(HeapRegionUsageTool.alignDown(value, 287 wb.getHeapSpaceAlignment()), 288 wb.psVirtualSpaceAlignment()); 289 case G1: 290 return HeapRegionUsageTool.alignUp(value, wb.g1RegionSize()); 291 default: 292 throw new RuntimeException("Unexpected young GC type"); 293 } 294 } 295 } 296 }