blob: 08a5b16fc3b7dde80274f0bd7aa71d2a653097d9 [file] [log] [blame]
/*
* Copyright (c) 2016, 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.
* @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<String> 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<String> options) throws Exception {
LinkedList<String> 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<String> options) throws Exception {
LinkedList<String> 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 = Utils.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<Object> garbage = new LinkedList<>();
public static void main(String args[]) throws Exception {
if (args.length != 3) {
throw new IllegalArgumentException("Expected 3 args: <minRatio> <maxRatio> <shrinkHeapInSteps>");
}
if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.PSOld) {
System.out.println("Test is not applicable to parallel GC");
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();
}
}
}