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