Path: blob/master/test/hotspot/jtreg/gc/arguments/TestTargetSurvivorRatioFlag.java
40948 views
/*1* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package gc.arguments;2425/*26* @test TestTargetSurvivorRatioFlag27* @summary Verify that option TargetSurvivorRatio affects survivor space occupancy after minor GC.28* @requires vm.opt.ExplicitGCInvokesConcurrent != true29* @requires vm.opt.UseJVMCICompiler != true30* @requires vm.gc != "Z" & vm.gc != "Shenandoah"31* @library /test/lib32* @library /33* @modules java.base/jdk.internal.misc34* java.management35* @build sun.hotspot.WhiteBox36* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox37* @run driver gc.arguments.TestTargetSurvivorRatioFlag38*/3940import java.lang.management.GarbageCollectorMXBean;41import java.util.Arrays;42import java.util.Collections;43import java.util.LinkedList;44import java.util.List;45import java.util.regex.Matcher;46import java.util.regex.Pattern;47import jdk.internal.misc.Unsafe;48import jdk.test.lib.process.OutputAnalyzer;49import jdk.test.lib.Utils;50import sun.hotspot.WhiteBox;51import static gc.testlibrary.Allocation.blackHole;5253/* In order to test that TargetSurvivorRatio affects survivor space occupancy54* we setup fixed MaxTenuringThreshold and then verifying that if size of allocated55* objects is lower than (survivor_size * TargetSurvivorRatio / 100) then objects56* will stay in survivor space until MaxTenuringThreshold minor GC cycles.57* If more than (survivor_size * TargetSurvivorRatio / 100) objects were allocated,58* then we verify that after MaxTenuringThreshold minor GC cycles survivor space59* is almost empty.60*/61public class TestTargetSurvivorRatioFlag {6263public static final long M = 1024 * 1024;6465// VM option values66public static final long MAX_NEW_SIZE = 40 * M;67public static final int SURVIVOR_RATIO = 8;68public static final int MAX_TENURING_THRESHOLD = 15;6970// Value used to estimate amount of memory that should be allocated71// and placed in survivor space.72public static final double DELTA = 0.25;7374// Max variance of observed ratio75public static double VARIANCE = 1;7677// Messages used by debuggee78public static final String UNSUPPORTED_GC = "Unsupported GC";79public static final String START_TEST = "Start test";80public static final String END_TEST = "End test";8182// Patterns used during log parsing83public static final String TENURING_DISTRIBUTION = "Desired survivor size";84public static final String AGE_TABLE_ENTRY = ".*-[\\s]+age[\\s]+([0-9]+):[\\s]+([0-9]+)[\\s]+bytes,[\\s]+([0-9]+)[\\s]+total";85public static final String MAX_SURVIVOR_SIZE = "Max survivor size: ([0-9]+)";8687public static void main(String args[]) throws Exception {8889LinkedList<String> options = new LinkedList<>(Arrays.asList(Utils.getTestJavaOpts()));9091// Need to consider the effect of TargetPLABWastePct=1 for G1 GC92if (options.contains("-XX:+UseG1GC")) {93VARIANCE = 2;94} else {95VARIANCE = 1;96}9798negativeTest(-1, options);99negativeTest(101, options);100101positiveTest(20, options);102positiveTest(30, options);103positiveTest(55, options);104positiveTest(70, options);105}106107/**108* Verify that VM will fail to start with specified TargetSurvivorRatio109*110* @param ratio value of TargetSurvivorRatio111* @param options additional VM options112*/113public static void negativeTest(int ratio, LinkedList<String> options) throws Exception {114LinkedList<String> vmOptions = new LinkedList<>(options);115vmOptions.add("-XX:TargetSurvivorRatio=" + ratio);116vmOptions.add("-version");117118ProcessBuilder procBuilder = GCArguments.createJavaProcessBuilder(vmOptions);119OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());120121analyzer.shouldHaveExitValue(1);122analyzer.shouldContain("Error: Could not create the Java Virtual Machine.");123}124125/**126* Verify that actual survivor space usage ratio conforms specified TargetSurvivorRatio127*128* @param ratio value of TargetSurvivorRatio129* @param options additional VM options130*/131public static void positiveTest(int ratio, LinkedList<String> options) throws Exception {132LinkedList<String> vmOptions = new LinkedList<>(options);133Collections.addAll(vmOptions,134"-Xbootclasspath/a:.",135"--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",136"-XX:+UnlockDiagnosticVMOptions",137"-XX:+WhiteBoxAPI",138"-XX:+UseAdaptiveSizePolicy",139"-Xlog:gc+age=trace",140"-XX:MaxTenuringThreshold=" + MAX_TENURING_THRESHOLD,141"-XX:NewSize=" + MAX_NEW_SIZE,142"-XX:MaxNewSize=" + MAX_NEW_SIZE,143"-XX:InitialHeapSize=" + 2 * MAX_NEW_SIZE,144"-XX:MaxHeapSize=" + 2 * MAX_NEW_SIZE,145"-XX:SurvivorRatio=" + SURVIVOR_RATIO,146"-XX:TargetSurvivorRatio=" + ratio,147// For reducing variance of survivor size.148"-XX:TargetPLABWastePct=" + 1,149TargetSurvivorRatioVerifier.class.getName(),150Integer.toString(ratio)151);152153ProcessBuilder procBuilder = GCArguments.createJavaProcessBuilder(vmOptions);154OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());155156analyzer.shouldHaveExitValue(0);157158String output = analyzer.getOutput();159160// Test avoids verification for parallel GC161if (!output.contains(UNSUPPORTED_GC)) {162// Two tests should be done - when actual ratio is lower than TargetSurvivorRatio163// and when it is higher. We chech that output contains results for exactly two tests.164List<Double> ratios = parseTestOutput(output);165166if (ratios.size() != 2) {167System.out.println(output);168throw new RuntimeException("Expected number of ratios extraced for output is 2,"169+ " but " + ratios.size() + " ratios were extracted");170}171172// At the end of the first test survivor space usage ratio should lies between173// TargetSurvivorRatio and TargetSurvivorRatio - 2*DELTA174if (ratio < ratios.get(0) || ratio - ratios.get(0) > VARIANCE) {175System.out.println(output);176throw new RuntimeException("Survivor space usage ratio expected to be close to "177+ ratio + ", but observed ratio is: " + ratios.get(0));178}179180// After second test survivor space should be almost empty.181if (ratios.get(1) > VARIANCE) {182System.out.println(output);183throw new RuntimeException("Survivor space expected to be empty due to "184+ "TargetSurvivorRatio overlimit, however observed "185+ "survivor space usage ratio is: " + ratios.get(1));186}187} else {188System.out.println("Selected GC does not support TargetSurvivorRatio option.");189}190}191192/**193* Parse output produced by TargetSurvivorRatioVerifier.194*195* @param output output obtained from TargetSurvivorRatioVerifier196* @return list of parsed test results, where each result is an actual197* survivor ratio after MaxTenuringThreshold minor GC cycles.198*/199public static List<Double> parseTestOutput(String output) {200List<Double> ratios = new LinkedList<Double>();201String lines[] = output.split("[\n\r]");202boolean testStarted = false;203long survivorSize = 0;204long survivorOccupancy = 0;205int gcCount = 0;206Pattern ageTableEntry = Pattern.compile(AGE_TABLE_ENTRY);207Pattern maxSurvivorSize = Pattern.compile(MAX_SURVIVOR_SIZE);208for (String line : lines) {209if (Pattern.matches(MAX_SURVIVOR_SIZE, line)) {210// We found estimated survivor space size211Matcher m = maxSurvivorSize.matcher(line);212m.find();213survivorSize = Long.valueOf(m.group(1));214} else if (line.contains(START_TEST) && !testStarted) {215// Start collecting test results216testStarted = true;217gcCount = 0;218} else if (testStarted) {219if (line.contains(TENURING_DISTRIBUTION)) {220// We found start of output emitted by -XX:+PrintTenuringDistribution221// If it is associated with "MaxTenuringThreshold" GC cycle, then it's222// time to report observed survivor usage ratio223gcCount++;224double survivorRatio = survivorOccupancy / (double) survivorSize;225if (gcCount == MAX_TENURING_THRESHOLD || gcCount == MAX_TENURING_THRESHOLD * 2) {226ratios.add(survivorRatio * 100.0);227testStarted = false;228}229survivorOccupancy = 0;230} else if (Pattern.matches(AGE_TABLE_ENTRY, line)) {231// Obtain survivor space usage from "total" age table log entry232Matcher m = ageTableEntry.matcher(line);233m.find();234survivorOccupancy = Long.valueOf(m.group(3));235} else if (line.contains(END_TEST)) {236// It is expected to find at least MaxTenuringThreshold GC events237// until test end238if (gcCount < MAX_TENURING_THRESHOLD) {239throw new RuntimeException("Observed " + gcCount + " GC events, "240+ "while it is expected to see at least "241+ MAX_TENURING_THRESHOLD);242}243testStarted = false;244}245}246}247return ratios;248}249250public static class TargetSurvivorRatioVerifier {251252static final WhiteBox wb = WhiteBox.getWhiteBox();253static final Unsafe unsafe = Unsafe.getUnsafe();254255// Desired size of memory allocated at once256public static final int CHUNK_SIZE = 1024;257// Length of byte[] array that will have occupy CHUNK_SIZE bytes in heap258public static final int ARRAY_LENGTH = CHUNK_SIZE - Unsafe.ARRAY_BYTE_BASE_OFFSET;259260public static void main(String args[]) throws Exception {261if (args.length != 1) {262throw new IllegalArgumentException("Expected 1 arg: <ratio>");263}264if (GCTypes.YoungGCType.getYoungGCType() == GCTypes.YoungGCType.PSNew) {265System.out.println(UNSUPPORTED_GC);266return;267}268269int ratio = Integer.valueOf(args[0]);270long maxSurvivorSize = getMaxSurvivorSize();271System.out.println("Max survivor size: " + maxSurvivorSize);272273allocateMemory(ratio - DELTA, maxSurvivorSize);274allocateMemory(ratio + DELTA, maxSurvivorSize);275}276277/**278* Allocate (<b>ratio</b> * <b>maxSize</b> / 100) bytes of objects279* and force at least "MaxTenuringThreshold" minor GCs.280*281* @param ratio ratio used to calculate how many objects should be allocated282* @param maxSize estimated max survivor space size283*/284public static void allocateMemory(double ratio, long maxSize) throws Exception {285GarbageCollectorMXBean youngGCBean = GCTypes.YoungGCType.getYoungGCBean();286long garbageSize = (long) (maxSize * (ratio / 100.0));287int arrayLength = (int) (garbageSize / CHUNK_SIZE);288AllocationHelper allocator = new AllocationHelper(1, arrayLength, ARRAY_LENGTH, null);289290System.out.println(START_TEST);291System.gc();292final long initialGcId = youngGCBean.getCollectionCount();293// allocate memory294allocator.allocateMemoryAndVerify();295296// force minor GC297while (youngGCBean.getCollectionCount() <= initialGcId + MAX_TENURING_THRESHOLD * 2) {298blackHole(new byte[ARRAY_LENGTH]);299}300301allocator.release();302System.out.println(END_TEST);303}304305/**306* Estimate max survivor space size.307*308* For non-G1 GC returns value reported by MemoryPoolMXBean309* associated with survivor space.310* For G1 GC return max number of survivor regions * region size.311* Number if survivor regions estimated from MaxNewSize and SurvivorRatio.312*/313public static long getMaxSurvivorSize() {314if (GCTypes.YoungGCType.getYoungGCType() == GCTypes.YoungGCType.G1) {315int youngLength = (int) Math.max(MAX_NEW_SIZE / wb.g1RegionSize(), 1);316return (long) Math.ceil(youngLength / (double) SURVIVOR_RATIO) * wb.g1RegionSize();317} else {318return HeapRegionUsageTool.getSurvivorUsage().getMax();319}320}321}322}323324325