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 }