/* * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test TestMaxMinHeapFreeRatioFlags * @key gc * @summary Verify that heap size changes according to max and min heap free ratios. * @requires vm.gc != "Z" * @library /test/lib * @modules java.base/jdk.internal.misc * java.management * @run driver/timeout=240 TestMaxMinHeapFreeRatioFlags */ import java.util.LinkedList; import java.util.Arrays; import java.util.Collections; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.Utils; import jdk.internal.misc.Unsafe; public class TestMaxMinHeapFreeRatioFlags { public static final long M = 1024 * 1024; public static final long MAX_HEAP_SIZE = 200 * M; public static final long HEAP_SIZE = 10 * M; public static final long MAX_NEW_SIZE = 20 * M; public static final long NEW_SIZE = 5 * M; public static void main(String args[]) throws Exception { LinkedList options = new LinkedList<>( Arrays.asList(Utils.getFilteredTestJavaOpts("-XX:[^ ]*HeapFreeRatio","-XX:\\+ExplicitGCInvokesConcurrent")) ); negativeTest(20, false, 10, true, options); negativeTest(100, true, 0, false, options); negativeTest(101, false, 50, false, options); negativeTest(49, true, 102, true, options); negativeTest(-1, false, 50, false, options); negativeTest(50, true, -1, true, options); positiveTest(10, false, 90, false, true, options); positiveTest(10, true, 80, false, true, options); positiveTest(20, false, 70, true, true, options); positiveTest(25, true, 65, true, true, options); positiveTest(40, false, 50, false, true, options); } /** * Verify that heap size will be changed to conform * min and max heap free ratios. * * @param minRatio value of MinHeapFreeRatio option * @param useXminf used Xminf option instead of MinHeapFreeRatio * @param maxRatio value of MaxHeapFreeRatio option * @param useXmaxf used Xmaxf option instead of MaxHeapFreeRatio * @param options additional options for JVM */ public static void positiveTest(int minRatio, boolean useXminf, int maxRatio, boolean useXmaxf, boolean shrinkHeapInSteps, LinkedList options) throws Exception { LinkedList vmOptions = new LinkedList<>(options); Collections.addAll(vmOptions, (useXminf ? "-Xminf" + minRatio / 100.0 : "-XX:MinHeapFreeRatio=" + minRatio), (useXmaxf ? "-Xmaxf" + maxRatio / 100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio), "-Xmx" + MAX_HEAP_SIZE, "-Xms" + HEAP_SIZE, "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", "-XX:NewSize=" + NEW_SIZE, "-XX:MaxNewSize=" + MAX_NEW_SIZE, "-XX:" + (shrinkHeapInSteps ? '+' : '-') + "ShrinkHeapInSteps", RatioVerifier.class.getName(), Integer.toString(minRatio), Integer.toString(maxRatio), Boolean.toString(shrinkHeapInSteps) ); ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()])); OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start()); analyzer.shouldHaveExitValue(0); } /** * Verify that VM will fail to start with specified ratios. * * @param minRatio value of MinHeapFreeRatio option * @param useXminf used Xminf option instead of MinHeapFreeRatio * @param maxRatio value of MaxHeapFreeRatio option * @param useXmaxf used Xmaxf option instead of MaxHeapFreeRatio * @param options additional options for JVM */ public static void negativeTest(int minRatio, boolean useXminf, int maxRatio, boolean useXmaxf, LinkedList options) throws Exception { LinkedList vmOptions = new LinkedList<>(options); Collections.addAll(vmOptions, (useXminf ? "-Xminf" + minRatio / 100.0 : "-XX:MinHeapFreeRatio=" + minRatio), (useXmaxf ? "-Xmaxf" + maxRatio / 100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio), "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", "-version" ); ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()])); OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start()); analyzer.shouldHaveExitValue(1); analyzer.shouldContain("Error: Could not create the Java Virtual Machine."); } /** * RatioVerifier will be executed in the tested VM. * It will check that real heap usage after collection lies between MinHeapFreeRatio and MaxHeapFreeRatio. */ public static class RatioVerifier { private static final Unsafe unsafe = Unsafe.getUnsafe(); // Size of byte array that will be allocated public static final int CHUNK_SIZE = 1024; // Length of byte array, that will be added to "garbage" list. public static final int ARRAY_LENGTH = CHUNK_SIZE - Unsafe.ARRAY_BYTE_BASE_OFFSET; // Amount of tries to force heap shrinking/expansion using GC public static final int GC_TRIES = 10; // Value that will be added/substracted from expected min/max heap free ratio // during memory allocation to make sure that specified limit will be exceeded. public static final double OVERLOAD = 0.05; // Acceptable heap free ratio limit exceedance: verification will fail if // actual ratio is lower than expected min heap free ratio - VARIANCE or // higher than expected max heap free ratio + VARIANCE. public static final double VARIANCE = 0.025; public static LinkedList garbage = new LinkedList<>(); public static void main(String args[]) throws Exception { if (args.length != 3) { throw new IllegalArgumentException("Expected 3 args: "); } if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.PSOld || GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) { System.out.println("Test is not applicable to parallel full GCs"); return; } double minRatio = Integer.valueOf(args[0]) / 100.0; double maxRatio = Integer.valueOf(args[1]) / 100.0; boolean shrinkHeapInSteps = Boolean.valueOf(args[2]); long maxHeapSize = getMax(); int gcTries = (shrinkHeapInSteps ? GC_TRIES : 1); // Initial checks. This also links up everything in these helper methods, // in case it brings more garbage. forceGC(gcTries); verifyRatio(minRatio, maxRatio); // commit 0.5 of total heap size to have enough space // to both shink and expand while (getCommitted() < maxHeapSize / 2) { garbage.add(new byte[ARRAY_LENGTH]); } forceGC(gcTries); // Verify that current heap free ratio lies between specified limits verifyRatio(minRatio, maxRatio); // Estimate how much memory we have to allocate to force expansion long memoryToFill = (long) (getCommitted() * (1 - minRatio + OVERLOAD)) - getUsed(); long previouslyCommitted = getCommitted(); while (memoryToFill > 0) { garbage.add(new byte[CHUNK_SIZE]); memoryToFill -= CHUNK_SIZE; } forceGC(gcTries); // Verify that after memory allocation heap free ratio is still conforming specified limits verifyRatio(minRatio, maxRatio); // Verify that heap was actually expanded if (previouslyCommitted >= getCommitted()) { throw new RuntimeException("Heap was not expanded."); } // Estimate how much memory we have to free to force shrinking long memoryToFree = getUsed() - (long) (getCommitted() * (1 - maxRatio - OVERLOAD)); previouslyCommitted = getCommitted(); while (memoryToFree > 0 && garbage.size() > 0) { garbage.remove(garbage.size() - 1); memoryToFree -= CHUNK_SIZE; } forceGC(gcTries); // Verify that heap free ratio is still conforming specified limits verifyRatio(minRatio, maxRatio); // Verify that heap was actually shrinked if (previouslyCommitted <= getCommitted()) { throw new RuntimeException("Heap was not shrinked."); } } public static void forceGC(int gcTries) { for (int i = 0; i < gcTries; i++) { System.gc(); try { Thread.sleep(10); } catch (InterruptedException ie) { } } } /** * Verify that heap free ratio is conforming specified limits. * Actual heap free ratio may be very close to one of specified limits, * but exceed for more then VARIANCE. * Verification will also pass if actual ratio is not conforming limits, * but it is not possible to shrink/expand heap. */ public static void verifyRatio(double minRatio, double maxRatio) { double ratio = getHeapFreeRatio(); System.out.println(minRatio + " " + ratio + " " + maxRatio); if (minRatio - ratio > VARIANCE && getCommitted() < getMax()) { throw new RuntimeException("Current heap free ratio is lower than " + "MinHeapFreeRatio (" + ratio + " vs " + minRatio + ")."); } if (ratio - maxRatio > VARIANCE && getUsed() > getInit()) { throw new RuntimeException("Current heap free ratio is higher than " + "MaxHeapFreeRatio (" + ratio + " vs " + maxRatio + ")."); } } /* * Obtain information about heap size. * * For G1 information summed up for all type of regions, * because tested options affect overall heap sizing. * * For all other GCs return information only for old gen. */ public static long getMax() { return HeapRegionUsageTool.getOldUsage().getMax(); } public static long getInit() { if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) { return HeapRegionUsageTool.getEdenUsage().getInit() + HeapRegionUsageTool.getSurvivorUsage().getInit() + HeapRegionUsageTool.getOldUsage().getInit(); } else { return HeapRegionUsageTool.getOldUsage().getInit(); } } public static long getUsed() { if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) { return HeapRegionUsageTool.getEdenUsage().getUsed() + HeapRegionUsageTool.getSurvivorUsage().getUsed() + HeapRegionUsageTool.getOldUsage().getUsed(); } else { return HeapRegionUsageTool.getOldUsage().getUsed(); } } public static long getCommitted() { if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) { return HeapRegionUsageTool.getEdenUsage().getCommitted() + HeapRegionUsageTool.getSurvivorUsage().getCommitted() + HeapRegionUsageTool.getOldUsage().getCommitted(); } else { return HeapRegionUsageTool.getOldUsage().getCommitted(); } } public static long getFree() { return getCommitted() - getUsed(); } public static double getHeapFreeRatio() { return getFree() / (double) getCommitted(); } } }