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.test.lib.unsafe.UnsafeHelper;
  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 = UnsafeHelper.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                 System.out.println("Test is not applicable to parallel GC");
 162                 return;
 163             }
 164 
 165             double minRatio = Integer.valueOf(args[0]) / 100.0;
 166             double maxRatio = Integer.valueOf(args[1]) / 100.0;
 167             boolean shrinkHeapInSteps = Boolean.valueOf(args[2]);
 168 
 169             long maxHeapSize = getMax();
 170             int gcTries = (shrinkHeapInSteps ? GC_TRIES : 1);
 171 
 172             // Initial checks. This also links up everything in these helper methods,
 173             // in case it brings more garbage.
 174             forceGC(gcTries);
 175             verifyRatio(minRatio, maxRatio);
 176 
 177             // commit 0.5 of total heap size to have enough space
 178             // to both shink and expand
 179             while (getCommitted() < maxHeapSize / 2) {
 180                 garbage.add(new byte[ARRAY_LENGTH]);
 181             }
 182 
 183             forceGC(gcTries);
 184             // Verify that current heap free ratio lies between specified limits
 185             verifyRatio(minRatio, maxRatio);
 186 
 187             // Estimate how much memory we have to allocate to force expansion
 188             long memoryToFill = (long) (getCommitted() * (1 - minRatio + OVERLOAD))
 189                     - getUsed();
 190 
 191             long previouslyCommitted = getCommitted();
 192 
 193             while (memoryToFill > 0) {
 194                 garbage.add(new byte[CHUNK_SIZE]);
 195                 memoryToFill -= CHUNK_SIZE;
 196             }
 197 
 198             forceGC(gcTries);
 199             // Verify that after memory allocation heap free ratio is still conforming specified limits
 200             verifyRatio(minRatio, maxRatio);
 201             // Verify that heap was actually expanded
 202             if (previouslyCommitted >= getCommitted()) {
 203                 throw new RuntimeException("Heap was not expanded.");
 204             }
 205 
 206             // Estimate how much memory we have to free to force shrinking
 207             long memoryToFree = getUsed()
 208                     - (long) (getCommitted() * (1 - maxRatio - OVERLOAD));
 209 
 210             previouslyCommitted = getCommitted();
 211 
 212             while (memoryToFree > 0 && garbage.size() > 0) {
 213                 garbage.remove(garbage.size() - 1);
 214                 memoryToFree -= CHUNK_SIZE;
 215             }
 216 
 217             forceGC(gcTries);
 218             // Verify that heap free ratio is still conforming specified limits
 219             verifyRatio(minRatio, maxRatio);
 220             // Verify that heap was actually shrinked
 221             if (previouslyCommitted <= getCommitted()) {
 222                 throw new RuntimeException("Heap was not shrinked.");
 223             }
 224         }
 225 
 226         public static void forceGC(int gcTries) {
 227             for (int i = 0; i < gcTries; i++) {
 228                 System.gc();
 229                 try {
 230                     Thread.sleep(10);
 231                 } catch (InterruptedException ie) {
 232                 }
 233             }
 234         }
 235 
 236         /**
 237          * Verify that heap free ratio is conforming specified limits.
 238          * Actual heap free ratio may be very close to one of specified limits,
 239          * but exceed for more then VARIANCE.
 240          * Verification will also pass if actual ratio is not conforming limits,
 241          * but it is not possible to shrink/expand heap.
 242          */
 243         public static void verifyRatio(double minRatio, double maxRatio) {
 244             double ratio = getHeapFreeRatio();
 245             System.out.println(minRatio + " " + ratio + " " + maxRatio);
 246             if (minRatio - ratio > VARIANCE
 247                     && getCommitted() < getMax()) {
 248                 throw new RuntimeException("Current heap free ratio is lower than "
 249                         + "MinHeapFreeRatio (" + ratio + " vs " + minRatio + ").");
 250             }
 251             if (ratio - maxRatio > VARIANCE
 252                     && getUsed() > getInit()) {
 253                 throw new RuntimeException("Current heap free ratio is higher than "
 254                         + "MaxHeapFreeRatio (" + ratio + " vs " + maxRatio + ").");
 255             }
 256         }
 257 
 258         /*
 259          * Obtain information about heap size.
 260          *
 261          * For G1 information summed up for all type of regions,
 262          * because tested options affect overall heap sizing.
 263          *
 264          * For all other GCs return information only for old gen.
 265          */
 266         public static long getMax() {
 267             return HeapRegionUsageTool.getOldUsage().getMax();
 268         }
 269 
 270         public static long getInit() {
 271             if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) {
 272                 return HeapRegionUsageTool.getEdenUsage().getInit()
 273                         + HeapRegionUsageTool.getSurvivorUsage().getInit()
 274                         + HeapRegionUsageTool.getOldUsage().getInit();
 275             } else {
 276                 return HeapRegionUsageTool.getOldUsage().getInit();
 277             }
 278         }
 279 
 280         public static long getUsed() {
 281             if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) {
 282                 return HeapRegionUsageTool.getEdenUsage().getUsed()
 283                         + HeapRegionUsageTool.getSurvivorUsage().getUsed()
 284                         + HeapRegionUsageTool.getOldUsage().getUsed();
 285             } else {
 286                 return HeapRegionUsageTool.getOldUsage().getUsed();
 287             }
 288         }
 289 
 290         public static long getCommitted() {
 291             if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) {
 292                 return HeapRegionUsageTool.getEdenUsage().getCommitted()
 293                         + HeapRegionUsageTool.getSurvivorUsage().getCommitted()
 294                         + HeapRegionUsageTool.getOldUsage().getCommitted();
 295             } else {
 296                 return HeapRegionUsageTool.getOldUsage().getCommitted();
 297             }
 298         }
 299 
 300         public static long getFree() {
 301             return getCommitted() - getUsed();
 302         }
 303 
 304         public static double getHeapFreeRatio() {
 305             return getFree() / (double) getCommitted();
 306         }
 307     }
 308 }