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