1 /*
   2 * Copyright (c) 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 TestMaxMinHeapFreeRatioFlags
  26  * @key gc
  27  * @summary Verify that heap size changes according to max and min heap free ratios.
  28  * @library /testlibrary
  29  * @modules java.base/jdk.internal.misc
  30  *          java.management
  31  * @build TestMaxMinHeapFreeRatioFlags
  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.OutputAnalyzer;
  39 import jdk.test.lib.ProcessTools;
  40 import jdk.test.lib.Utils;
  41 import jdk.test.lib.HeapRegionUsageTool;
  42 import jdk.internal.misc.Unsafe;
  43 
  44 public class TestMaxMinHeapFreeRatioFlags {
  45 
  46     public static final long M = 1024 * 1024;
  47     public static final long MAX_HEAP_SIZE = 200 * M;
  48     public static final long HEAP_SIZE = 10 * M;
  49     public static final long MAX_NEW_SIZE = 20 * M;
  50     public static final long NEW_SIZE = 5 * M;
  51 
  52     public static void main(String args[]) throws Exception {
  53         LinkedList<String> options = new LinkedList<>(
  54                 Arrays.asList(Utils.getFilteredTestJavaOpts("-XX:[^ ]*HeapFreeRatio","-XX:\\+ExplicitGCInvokesConcurrent"))
  55         );
  56 
  57         negativeTest(20, false, 10, true, options);
  58         negativeTest(100, true, 0, false, options);
  59         negativeTest(101, false, 50, false, options);
  60         negativeTest(49, true, 102, true, options);
  61         negativeTest(-1, false, 50, false, options);
  62         negativeTest(50, true, -1, true, options);
  63 
  64         positiveTest(10, false, 90, false, true, options);
  65         positiveTest(10, true, 80, false, true, options);
  66         positiveTest(20, false, 70, true, true, options);
  67         positiveTest(25, true, 65, true, true, options);
  68         positiveTest(40, false, 50, false, true, options);
  69     }
  70 
  71     /**
  72      * Verify that heap size will be changed to conform
  73      * min and max heap free ratios.
  74      *
  75      * @param minRatio value of MinHeapFreeRatio option
  76      * @param useXminf used Xminf option instead of MinHeapFreeRatio
  77      * @param maxRatio value of MaxHeapFreeRatio option
  78      * @param useXmaxf used Xmaxf option instead of MaxHeapFreeRatio
  79      * @param options additional options for JVM
  80      */
  81     public static void positiveTest(int minRatio, boolean useXminf,
  82             int maxRatio, boolean useXmaxf, boolean shrinkHeapInSteps,
  83             LinkedList<String> options) throws Exception {
  84 
  85         LinkedList<String> vmOptions = new LinkedList<>(options);
  86         Collections.addAll(vmOptions,
  87                 (useXminf ? "-Xminf" + minRatio / 100.0 : "-XX:MinHeapFreeRatio=" + minRatio),
  88                 (useXmaxf ? "-Xmaxf" + maxRatio / 100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio),
  89                 "-Xmx" + MAX_HEAP_SIZE,
  90                 "-Xms" + HEAP_SIZE,
  91                 "-XaddExports:java.base/jdk.internal.misc=ALL-UNNAMED",
  92                 "-XX:NewSize=" + NEW_SIZE,
  93                 "-XX:MaxNewSize=" + MAX_NEW_SIZE,
  94                 "-XX:" + (shrinkHeapInSteps ? '+' : '-') + "ShrinkHeapInSteps",
  95                 RatioVerifier.class.getName(),
  96                 Integer.toString(minRatio),
  97                 Integer.toString(maxRatio),
  98                 Boolean.toString(shrinkHeapInSteps)
  99         );
 100 
 101         ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
 102         OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
 103         analyzer.shouldHaveExitValue(0);
 104     }
 105 
 106     /**
 107      * Verify that VM will fail to start with specified ratios.
 108      *
 109      * @param minRatio value of MinHeapFreeRatio option
 110      * @param useXminf used Xminf option instead of MinHeapFreeRatio
 111      * @param maxRatio value of MaxHeapFreeRatio option
 112      * @param useXmaxf used Xmaxf option instead of MaxHeapFreeRatio
 113      * @param options additional options for JVM
 114      */
 115     public static void negativeTest(int minRatio, boolean useXminf,
 116             int maxRatio, boolean useXmaxf,
 117             LinkedList<String> options) throws Exception {
 118 
 119         LinkedList<String> vmOptions = new LinkedList<>(options);
 120         Collections.addAll(vmOptions,
 121                 (useXminf ? "-Xminf" + minRatio / 100.0 : "-XX:MinHeapFreeRatio=" + minRatio),
 122                 (useXmaxf ? "-Xmaxf" + maxRatio / 100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio),
 123                 "-XaddExports:java.base/jdk.internal.misc=ALL-UNNAMED",
 124                 "-version"
 125         );
 126         ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
 127         OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
 128         analyzer.shouldHaveExitValue(1);
 129         analyzer.shouldContain("Error: Could not create the Java Virtual Machine.");
 130     }
 131 
 132     /**
 133      * RatioVerifier will be executed in the tested VM.
 134      * It will check that real heap usage after collection lies between MinHeapFreeRatio and MaxHeapFreeRatio.
 135      */
 136     public static class RatioVerifier {
 137 
 138         private static final Unsafe unsafe = Utils.getUnsafe();
 139 
 140         // Size of byte array that will be allocated
 141         public static final int CHUNK_SIZE = 1024;
 142         // Length of byte array, that will be added to "garbage" list.
 143         public static final int ARRAY_LENGTH = CHUNK_SIZE - Unsafe.ARRAY_BYTE_BASE_OFFSET;
 144         // Amount of tries to force heap shrinking/expansion using GC
 145         public static final int GC_TRIES = 10;
 146 
 147         // Value that will be added/substracted from expected min/max heap free ratio
 148         // during memory allocation to make sure that specified limit will be exceeded.
 149         public static final double OVERLOAD = 0.05;
 150         // Acceptable heap free ratio limit exceedance: verification will fail if
 151         // actual ratio is lower than expected min heap free ratio - VARIANCE or
 152         // higher than expected max heap free ratio + VARIANCE.
 153         public static final double VARIANCE = 0.025;
 154 
 155         public static LinkedList<Object> garbage = new LinkedList<>();
 156 
 157         public static void main(String args[]) throws Exception {
 158             if (args.length != 3) {
 159                 throw new IllegalArgumentException("Expected 3 args: <minRatio> <maxRatio> <shrinkHeapInSteps>");
 160             }
 161             if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.PSOld) {
 162                 System.out.println("Test is not applicable to parallel GC");
 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 }