/* * Copyright (c) 2013, 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 * @bug 8025166 * @summary Verify that heap size changes according to max and min heap free ratios. * @library /testlibrary /testlibrary/gc * @build TestMaxMinHeapFreeRatioFlags TestRatioTool * @run main/othervm/timeout=240 TestMaxMinHeapFreeRatioFlags */ import java.util.LinkedList; import java.lang.reflect.Field; import java.lang.management.MemoryUsage; import sun.misc.Unsafe; import com.oracle.java.testlibrary.*; public class TestMaxMinHeapFreeRatioFlags { public static final long M = 1024 * 1024; public static final long MaxHeapSize = 200 * M; public static final long HeapSize = 10 * M; public static final long MaxNewSize = 20 * M; public static final long NewSize = 5 * M; public static void main(String args[]) throws Exception { String options = System.getProperty("test.java.opts"); if (options == null) { options = ""; } else { options.replaceAll("-XX:[^ ]*HeapFreeRatio",""); } negativeTest(20, false, 10, true, options); negativeTest(20, true, 10, false, options); negativeTest(101, false, 102, false, options); negativeTest(101, true, 102, true, options); negativeTest(-1, false, 50, false, options); negativeTest(-1, true, 50, true, options); positiveTest(10, false, 90, false, options); positiveTest(10, true, 80, false, options); positiveTest(20, false, 70, true, options); positiveTest(25, true, 65, true, options); positiveTest(40, false, 50, false, 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 useXminf used Xmaxf option instead of MaxHeapFreeRatio * @param options additional options for JVM */ public static void positiveTest(int minRatio, boolean useXminf, int maxRatio, boolean useXmaxf, String options) throws Exception { options += " " + (useXminf ? "-Xminf" + minRatio/100.0 : "-XX:MinHeapFreeRatio=" + minRatio) + " " + (useXmaxf ? "-Xmaxf" + maxRatio/100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio) + " -Xmx" + MaxHeapSize + " -Xms" + HeapSize + " -XX:NewSize=" + NewSize + " -XX:MaxNewSize=" + MaxNewSize +" " + RatioVerifier.class.getName() + " " + minRatio + " " + maxRatio; options = options.replaceAll("[\\s]+", " ").trim(); ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(options.split(" ")); 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 useXminf used Xmaxf option instead of MaxHeapFreeRatio * @param options additional options for JVM */ public static void negativeTest(int minRatio, boolean useXminf, int maxRatio, boolean useXmaxf, String options) throws Exception { options += " " + (useXminf ? "-Xminf" + minRatio/100.0 : "-XX:MinHeapFreeRatio=" + minRatio) + " " + (useXmaxf ? "-Xmaxf" + maxRatio/100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio) + " -version"; options = options.replaceAll("[\\s]+", " ").trim(); ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(options.split(" ")); OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start()); analyzer.shouldHaveExitValue(1); analyzer.shouldContain("Error: Could not create the Java Virtual Machine."); } public static class RatioVerifier { private static Unsafe unsafe = null; static { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); unsafe = (Unsafe) theUnsafe.get(null); } catch (NoSuchFieldException | IllegalAccessException e) { throw new IllegalStateException(e); } } //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 (TestRatioTool.getOldGCType() == TestRatioTool.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; long maxHeapSize = getMax(); long previouslyCommitted = 0; //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(); //Verify that current heap free ratio lies between specified limits verifyRatio(minRatio, maxRatio); //Estimeate how much memory we have to allocate to force expansion long memoryToFill = (long)(getCommitted()*(1-minRatio+OVERLOAD)) - getUsed(); previouslyCommitted = getCommitted(); while (memoryToFill > 0) { garbage.add(new byte[1024]); memoryToFill -= CHUNK_SIZE; } forceGC(); //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(); //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() throws Exception { for (int i = 0; i < GC_TRIES; i++) { System.gc(); Thread.sleep(10); } } /** * Verify that heap free ratio is conforming specified limites. * 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 shink/expand heap. */ public static void verifyRatio(double minRatio, double maxRatio) throws Exception { 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 TestRatioTool.getOldUsage().getMax(); } public static long getInit() { if (TestRatioTool.getOldGCType() == TestRatioTool.OldGCType.G1) { return TestRatioTool.getEdenUsage().getInit() + TestRatioTool.getSurvivorUsage().getInit() + TestRatioTool.getOldUsage().getInit(); } else { return TestRatioTool.getOldUsage().getInit(); } } public static long getUsed() { if (TestRatioTool.getOldGCType() == TestRatioTool.OldGCType.G1) { return TestRatioTool.getEdenUsage().getUsed() + TestRatioTool.getSurvivorUsage().getUsed() + TestRatioTool.getOldUsage().getUsed(); } else { return TestRatioTool.getOldUsage().getUsed(); } } public static long getCommitted() { if (TestRatioTool.getOldGCType() == TestRatioTool.OldGCType.G1) { return TestRatioTool.getEdenUsage().getCommitted() + TestRatioTool.getSurvivorUsage().getCommitted() + TestRatioTool.getOldUsage().getCommitted(); } else { return TestRatioTool.getOldUsage().getCommitted(); } } public static long getFree() { return getCommitted() - getUsed(); } public static double getHeapFreeRatio() { return getFree()/(double)getCommitted(); } } }