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