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 }