1 /*
   2  * Copyright (c) 2016, 2018, 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 TestMaxMinHeapFreeRatioFlags
  26  * @key gc
  27  * @summary Verify that heap size changes according to max and min heap free ratios.
  28  * @requires vm.gc != "Z" & vm.gc != "Shenandoah"
  29  * @library /test/lib
  30  * @modules java.base/jdk.internal.misc
  31  *          java.management
  32  * @run driver/timeout=240 TestMaxMinHeapFreeRatioFlags
  33  */
  34 
  35 import java.util.LinkedList;
  36 import java.util.Arrays;
  37 import java.util.Collections;
  38 import jdk.test.lib.process.OutputAnalyzer;
  39 import jdk.test.lib.process.ProcessTools;
  40 import jdk.test.lib.Utils;
  41 import jdk.internal.misc.Unsafe;
  42 
  43 public class TestMaxMinHeapFreeRatioFlags {
  44 
  45     public static final long M = 1024 * 1024;
  46     public static final long MAX_HEAP_SIZE = 200 * M;
  47     public static final long HEAP_SIZE = 10 * M;
  48     public static final long MAX_NEW_SIZE = 20 * M;
  49     public static final long NEW_SIZE = 5 * M;
  50 
  51     public static void main(String args[]) throws Exception {
  52         LinkedList<String> options = new LinkedList<>(
  53                 Arrays.asList(Utils.getFilteredTestJavaOpts("-XX:[^ ]*HeapFreeRatio","-XX:\\+ExplicitGCInvokesConcurrent"))
  54         );
  55 
  56         negativeTest(20, false, 10, true, options);
  57         negativeTest(100, true, 0, false, options);
  58         negativeTest(101, false, 50, false, options);
  59         negativeTest(49, true, 102, true, options);
  60         negativeTest(-1, false, 50, false, options);
  61         negativeTest(50, true, -1, true, options);
  62 
  63         positiveTest(10, false, 90, false, true, options);
  64         positiveTest(10, true, 80, false, true, options);
  65         positiveTest(20, false, 70, true, true, options);
  66         positiveTest(25, true, 65, true, true, options);
  67         positiveTest(40, false, 50, false, true, options);
  68     }
  69 
  70     /**
  71      * Verify that heap size will be changed to conform
  72      * min and max heap free ratios.
  73      *
  74      * @param minRatio value of MinHeapFreeRatio option
  75      * @param useXminf used Xminf option instead of MinHeapFreeRatio
  76      * @param maxRatio value of MaxHeapFreeRatio option
  77      * @param useXmaxf used Xmaxf option instead of MaxHeapFreeRatio
  78      * @param options additional options for JVM
  79      */
  80     public static void positiveTest(int minRatio, boolean useXminf,
  81             int maxRatio, boolean useXmaxf, boolean shrinkHeapInSteps,
  82             LinkedList<String> options) throws Exception {
  83 
  84         LinkedList<String> vmOptions = new LinkedList<>(options);
  85         Collections.addAll(vmOptions,
  86                 (useXminf ? "-Xminf" + minRatio / 100.0 : "-XX:MinHeapFreeRatio=" + minRatio),
  87                 (useXmaxf ? "-Xmaxf" + maxRatio / 100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio),
  88                 "-Xmx" + MAX_HEAP_SIZE,
  89                 "-Xms" + HEAP_SIZE,
  90                 "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
  91                 "-XX:NewSize=" + NEW_SIZE,
  92                 "-XX:MaxNewSize=" + MAX_NEW_SIZE,
  93                 "-XX:" + (shrinkHeapInSteps ? '+' : '-') + "ShrinkHeapInSteps",
  94                 RatioVerifier.class.getName(),
  95                 Integer.toString(minRatio),
  96                 Integer.toString(maxRatio),
  97                 Boolean.toString(shrinkHeapInSteps)
  98         );
  99 
 100         ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
 101         OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
 102         analyzer.shouldHaveExitValue(0);
 103     }
 104 
 105     /**
 106      * Verify that VM will fail to start with specified ratios.
 107      *
 108      * @param minRatio value of MinHeapFreeRatio option
 109      * @param useXminf used Xminf option instead of MinHeapFreeRatio
 110      * @param maxRatio value of MaxHeapFreeRatio option
 111      * @param useXmaxf used Xmaxf option instead of MaxHeapFreeRatio
 112      * @param options additional options for JVM
 113      */
 114     public static void negativeTest(int minRatio, boolean useXminf,
 115             int maxRatio, boolean useXmaxf,
 116             LinkedList<String> options) throws Exception {
 117 
 118         LinkedList<String> vmOptions = new LinkedList<>(options);
 119         Collections.addAll(vmOptions,
 120                 (useXminf ? "-Xminf" + minRatio / 100.0 : "-XX:MinHeapFreeRatio=" + minRatio),
 121                 (useXmaxf ? "-Xmaxf" + maxRatio / 100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio),
 122                 "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
 123                 "-version"
 124         );
 125         ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
 126         OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
 127         analyzer.shouldHaveExitValue(1);
 128         analyzer.shouldContain("Error: Could not create the Java Virtual Machine.");
 129     }
 130 
 131     /**
 132      * RatioVerifier will be executed in the tested VM.
 133      * It will check that real heap usage after collection lies between MinHeapFreeRatio and MaxHeapFreeRatio.
 134      */
 135     public static class RatioVerifier {
 136 
 137         private static final Unsafe unsafe = Unsafe.getUnsafe();
 138 
 139         // Size of byte array that will be allocated
 140         public static final int CHUNK_SIZE = 1024;
 141         // Length of byte array, that will be added to "garbage" list.
 142         public static final int ARRAY_LENGTH = CHUNK_SIZE - Unsafe.ARRAY_BYTE_BASE_OFFSET;
 143         // Amount of tries to force heap shrinking/expansion using GC
 144         public static final int GC_TRIES = 10;
 145 
 146         // Value that will be added/substracted from expected min/max heap free ratio
 147         // during memory allocation to make sure that specified limit will be exceeded.
 148         public static final double OVERLOAD = 0.05;
 149         // Acceptable heap free ratio limit exceedance: verification will fail if
 150         // actual ratio is lower than expected min heap free ratio - VARIANCE or
 151         // higher than expected max heap free ratio + VARIANCE.
 152         public static final double VARIANCE = 0.025;
 153 
 154         public static LinkedList<Object> garbage = new LinkedList<>();
 155 
 156         public static void main(String args[]) throws Exception {
 157             if (args.length != 3) {
 158                 throw new IllegalArgumentException("Expected 3 args: <minRatio> <maxRatio> <shrinkHeapInSteps>");
 159             }
 160             if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.PSOld ||
 161                 GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) {
 162                 System.out.println("Test is not applicable to parallel full GCs");
 163                 return;
 164             }
 165 
 166             double minRatio = Integer.valueOf(args[0]) / 100.0;
 167             double maxRatio = Integer.valueOf(args[1]) / 100.0;
 168             boolean shrinkHeapInSteps = Boolean.valueOf(args[2]);
 169 
 170             long maxHeapSize = getMax();
 171             int gcTries = (shrinkHeapInSteps ? GC_TRIES : 1);
 172 
 173             // Initial checks. This also links up everything in these helper methods,
 174             // in case it brings more garbage.
 175             forceGC(gcTries);
 176             verifyRatio(minRatio, maxRatio);
 177 
 178             // commit 0.5 of total heap size to have enough space
 179             // to both shink and expand
 180             while (getCommitted() < maxHeapSize / 2) {
 181                 garbage.add(new byte[ARRAY_LENGTH]);
 182             }
 183 
 184             forceGC(gcTries);
 185             // Verify that current heap free ratio lies between specified limits
 186             verifyRatio(minRatio, maxRatio);
 187 
 188             // Estimate how much memory we have to allocate to force expansion
 189             long memoryToFill = (long) (getCommitted() * (1 - minRatio + OVERLOAD))
 190                     - getUsed();
 191 
 192             long previouslyCommitted = getCommitted();
 193 
 194             while (memoryToFill > 0) {
 195                 garbage.add(new byte[CHUNK_SIZE]);
 196                 memoryToFill -= CHUNK_SIZE;
 197             }
 198 
 199             forceGC(gcTries);
 200             // Verify that after memory allocation heap free ratio is still conforming specified limits
 201             verifyRatio(minRatio, maxRatio);
 202             // Verify that heap was actually expanded
 203             if (previouslyCommitted >= getCommitted()) {
 204                 throw new RuntimeException("Heap was not expanded.");
 205             }
 206 
 207             // Estimate how much memory we have to free to force shrinking
 208             long memoryToFree = getUsed()
 209                     - (long) (getCommitted() * (1 - maxRatio - OVERLOAD));
 210 
 211             previouslyCommitted = getCommitted();
 212 
 213             while (memoryToFree > 0 && garbage.size() > 0) {
 214                 garbage.remove(garbage.size() - 1);
 215                 memoryToFree -= CHUNK_SIZE;
 216             }
 217 
 218             forceGC(gcTries);
 219             // Verify that heap free ratio is still conforming specified limits
 220             verifyRatio(minRatio, maxRatio);
 221             // Verify that heap was actually shrinked
 222             if (previouslyCommitted <= getCommitted()) {
 223                 throw new RuntimeException("Heap was not shrinked.");
 224             }
 225         }
 226 
 227         public static void forceGC(int gcTries) {
 228             for (int i = 0; i < gcTries; i++) {
 229                 System.gc();
 230                 try {
 231                     Thread.sleep(10);
 232                 } catch (InterruptedException ie) {
 233                 }
 234             }
 235         }
 236 
 237         /**
 238          * Verify that heap free ratio is conforming specified limits.
 239          * Actual heap free ratio may be very close to one of specified limits,
 240          * but exceed for more then VARIANCE.
 241          * Verification will also pass if actual ratio is not conforming limits,
 242          * but it is not possible to shrink/expand heap.
 243          */
 244         public static void verifyRatio(double minRatio, double maxRatio) {
 245             double ratio = getHeapFreeRatio();
 246             System.out.println(minRatio + " " + ratio + " " + maxRatio);
 247             if (minRatio - ratio > VARIANCE
 248                     && getCommitted() < getMax()) {
 249                 throw new RuntimeException("Current heap free ratio is lower than "
 250                         + "MinHeapFreeRatio (" + ratio + " vs " + minRatio + ").");
 251             }
 252             if (ratio - maxRatio > VARIANCE
 253                     && getUsed() > getInit()) {
 254                 throw new RuntimeException("Current heap free ratio is higher than "
 255                         + "MaxHeapFreeRatio (" + ratio + " vs " + maxRatio + ").");
 256             }
 257         }
 258 
 259         /*
 260          * Obtain information about heap size.
 261          *
 262          * For G1 information summed up for all type of regions,
 263          * because tested options affect overall heap sizing.
 264          *
 265          * For all other GCs return information only for old gen.
 266          */
 267         public static long getMax() {
 268             return HeapRegionUsageTool.getOldUsage().getMax();
 269         }
 270 
 271         public static long getInit() {
 272             if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) {
 273                 return HeapRegionUsageTool.getEdenUsage().getInit()
 274                         + HeapRegionUsageTool.getSurvivorUsage().getInit()
 275                         + HeapRegionUsageTool.getOldUsage().getInit();
 276             } else {
 277                 return HeapRegionUsageTool.getOldUsage().getInit();
 278             }
 279         }
 280 
 281         public static long getUsed() {
 282             if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) {
 283                 return HeapRegionUsageTool.getEdenUsage().getUsed()
 284                         + HeapRegionUsageTool.getSurvivorUsage().getUsed()
 285                         + HeapRegionUsageTool.getOldUsage().getUsed();
 286             } else {
 287                 return HeapRegionUsageTool.getOldUsage().getUsed();
 288             }
 289         }
 290 
 291         public static long getCommitted() {
 292             if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) {
 293                 return HeapRegionUsageTool.getEdenUsage().getCommitted()
 294                         + HeapRegionUsageTool.getSurvivorUsage().getCommitted()
 295                         + HeapRegionUsageTool.getOldUsage().getCommitted();
 296             } else {
 297                 return HeapRegionUsageTool.getOldUsage().getCommitted();
 298             }
 299         }
 300 
 301         public static long getFree() {
 302             return getCommitted() - getUsed();
 303         }
 304 
 305         public static double getHeapFreeRatio() {
 306             return getFree() / (double) getCommitted();
 307         }
 308     }
 309 }