--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/g1/plab/TestPLABPromotion.java Wed Jan 27 18:22:25 2016 +0300
@@ -0,0 +1,327 @@
+/*
+ * 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 TestPLABPromotion
+ * @bug 8141278
+ * @summary Test PLAB promotion
+ * @requires vm.gc=="G1" | vm.gc=="null"
+ * @library /testlibrary /../../test/lib /
+ * @modules java.management
+ * @build ClassFileInstaller
+ * sun.hotspot.WhiteBox
+ * gc.g1.plab.lib.MemoryConsumer
+ * gc.g1.plab.lib.LogParser
+ * gc.g1.plab.lib.AppPLABPromotion
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main gc.g1.plab.TestPLABPromotion
+ */
+package gc.g1.plab;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Arrays;
+import java.io.PrintStream;
+
+import gc.g1.plab.lib.AppPLABPromotion;
+import gc.g1.plab.lib.LogParser;
+import gc.g1.plab.lib.PLABUtils;
+
+import jdk.test.lib.OutputAnalyzer;
+import jdk.test.lib.ProcessTools;
+import jdk.test.lib.Platform;
+
+/**
+ * Test checks PLAB promotion of different size objects.
+ */
+public class TestPLABPromotion {
+
+ // GC ID with survivor PLAB statistics
+ private final static long GC_ID_SURVIVOR_STATS = 1l;
+ // GC ID with old PLAB statistics
+ private final static long GC_ID_OLD_STATS = 2l;
+
+ // Threshold to determine whether the correct amount of objects were promoted.
+ // This is only an approximate threshold for these checks.
+ private final static long MEM_CONSUMPTION_THRESHOLD = 256l * 1024l;
+
+ private static final int PLAB_SIZE_SMALL = 1024;
+ private static final int PLAB_SIZE_MEDIUM = 4096;
+ private static final int PLAB_SIZE_HIGH = 65536;
+ private static final int OBJECT_SIZE_SMALL = 10;
+ private static final int OBJECT_SIZE_MEDIUM = 100;
+ private static final int OBJECT_SIZE_HIGH = 1000;
+ private static final int GC_NUM_SMALL = 1;
+ private static final int GC_NUM_MEDIUM = 3;
+ private static final int GC_NUM_HIGH = 7;
+ private static final int WASTE_PCT_SMALL = 10;
+ private static final int WASTE_PCT_MEDIUM = 20;
+ private static final int WASTE_PCT_HIGH = 30;
+ private static final int YOUNG_SIZE_LOW = 16;
+ private static final int YOUNG_SIZE_HIGH = 64;
+ private static final boolean PLAB_FIXED = true;
+ private static final boolean PLAB_DYNAMIC = false;
+
+ private final static TestCase[] TEST_CASES = {
+ // Test cases for unreachable object, PLAB size is fixed
+ new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, false, false),
+ new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, false, false),
+ // Test cases for reachable objects, PLAB size is fixed
+ new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, true, true),
+ new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_MEDIUM, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, true, true),
+ new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, PLAB_FIXED, true, false),
+ new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, true, true),
+ new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, true, true),
+ new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, PLAB_FIXED, true, true),
+ new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_SMALL, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, true, true),
+ new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, true, true),
+ new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_HIGH, PLAB_FIXED, true, false),
+ // Test cases for unreachable object, PLAB size is not fixed
+ new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_LOW, PLAB_DYNAMIC, false, false),
+ // Test cases for reachable objects, PLAB size is not fixed
+ new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_DYNAMIC, true, true),
+ new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_DYNAMIC, true, true),
+ new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_DYNAMIC, true, false),
+ new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, PLAB_DYNAMIC, true, true),
+ new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_HIGH, PLAB_DYNAMIC, true, true),
+ new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_LOW, PLAB_DYNAMIC, true, true)
+ };
+
+ public static void main(String[] args) throws Throwable {
+
+ for (TestCase testCase : TEST_CASES) {
+ // What we going to check.
+ testCase.print(System.out);
+ List<String> options = PLABUtils.prepareOptions(testCase.toOptions());
+ options.add(AppPLABPromotion.class.getName());
+ OutputAnalyzer out = ProcessTools.executeTestJvm(options.toArray(new String[options.size()]));
+ if (out.getExitValue() != 0) {
+ System.out.println(out.getOutput());
+ throw new RuntimeException("Expect exit code 0.");
+ }
+ checkResults(out.getOutput(), testCase);
+ }
+ }
+
+ private static void checkResults(String output, TestCase testCase) {
+ long plabAllocatedSurvivor;
+ long directAllocatedSurvivor;
+ long plabAllocatedOld;
+ long directAllocatedOld;
+ long memAllocated = testCase.getMemToFill();
+ long wordSize = Platform.is32bit() ? 4l : 8l;
+ LogParser logParser = new LogParser(output);
+
+ Map<String, Long> survivorStats = getPlabStats(logParser, LogParser.ReportType.SURVIVOR_STATS, GC_ID_SURVIVOR_STATS);
+ Map<String, Long> oldStats = getPlabStats(logParser, LogParser.ReportType.OLD_STATS, GC_ID_OLD_STATS);
+
+ plabAllocatedSurvivor = wordSize * survivorStats.get("used");
+ directAllocatedSurvivor = wordSize * survivorStats.get("direct_allocated");
+ plabAllocatedOld = wordSize * oldStats.get("used");
+ directAllocatedOld = wordSize * oldStats.get("direct_allocated");
+
+ System.out.printf("Survivor PLAB allocated:%17d Direct allocated: %17d Mem consumed:%17d%n", plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated);
+ System.out.printf("Old PLAB allocated:%17d Direct allocated: %17d Mem consumed:%17d%n", plabAllocatedOld, directAllocatedOld, memAllocated);
+
+ // Unreachable objects case
+ if (testCase.isDeadObjectCase()) {
+ // No dead objects should be promoted
+ if (plabAllocatedSurvivor > MEM_CONSUMPTION_THRESHOLD || directAllocatedSurvivor > MEM_CONSUMPTION_THRESHOLD) {
+ System.out.println(output);
+ throw new RuntimeException("Unreachable objects should not be allocated using PLAB or direct allocated to Survivor");
+ }
+ if (plabAllocatedOld > MEM_CONSUMPTION_THRESHOLD || directAllocatedOld > MEM_CONSUMPTION_THRESHOLD) {
+ System.out.println(output);
+ throw new RuntimeException("Unreachable objects should not be allocated using PLAB or direct allocated to Old");
+ }
+ } else {
+ // Live objects case
+ if (testCase.isPromotedByPLAB()) {
+ // All live small objects should be promoted using PLAB
+ if (Math.abs(plabAllocatedSurvivor - memAllocated) > MEM_CONSUMPTION_THRESHOLD) {
+ System.out.println(output);
+ throw new RuntimeException("Expect that Survivor PLAB allocation are similar to all mem consumed");
+ }
+ if (Math.abs(plabAllocatedOld - memAllocated) > MEM_CONSUMPTION_THRESHOLD) {
+ System.out.println(output);
+ throw new RuntimeException("Expect that Old PLAB allocation are similar to all mem consumed");
+ }
+ } else {
+ // All big objects should be directly allocated
+ if (Math.abs(directAllocatedSurvivor - memAllocated) > MEM_CONSUMPTION_THRESHOLD) {
+ System.out.println(output);
+ throw new RuntimeException("Test fails. Expect that Survivor direct allocation are similar to all mem consumed");
+ }
+ if (Math.abs(directAllocatedOld - memAllocated) > MEM_CONSUMPTION_THRESHOLD) {
+ System.out.println(output);
+ throw new RuntimeException("Test fails. Expect that Old direct allocation are similar to all mem consumed");
+ }
+ }
+
+ // All promoted objects size should be similar to all consumed memory
+ if (Math.abs(plabAllocatedSurvivor + directAllocatedSurvivor - memAllocated) > MEM_CONSUMPTION_THRESHOLD) {
+ System.out.println(output);
+ throw new RuntimeException("Test fails. Expect that Survivor gen total allocation are similar to all mem consumed");
+ }
+ if (Math.abs(plabAllocatedOld + directAllocatedOld - memAllocated) > MEM_CONSUMPTION_THRESHOLD) {
+ System.out.println(output);
+ throw new RuntimeException("Test fails. Expect that Old gen total allocation are similar to all mem consumed");
+ }
+ }
+ System.out.println("Test passed!");
+ }
+
+ private static Map<String, Long> getPlabStats(LogParser logParser, LogParser.ReportType type, long gc_id) {
+
+ Map<String, Long> survivorStats = logParser.getEntries()
+ .get(gc_id)
+ .get(type);
+ return survivorStats;
+ }
+
+ /**
+ * Description of one test case.
+ */
+ private static class TestCase {
+
+ private final int wastePct;
+ private final int plabSize;
+ private final int chunkSize;
+ private final int parGCThreads;
+ private final int edenSize;
+ private final boolean plabIsFixed;
+ private final boolean objectsAreReachable;
+ private final boolean promotedByPLAB;
+
+ /**
+ * @param wastePct
+ * ParallelGCBufferWastePct
+ * @param plabSize
+ * -XX:OldPLABSize and -XX:YoungPLABSize
+ * @param chunkSize
+ * requested object size for memory consumption
+ * @param parGCThreads
+ * -XX:ParallelGCThreads
+ * @param edenSize
+ * NewSize and MaxNewSize
+ * @param plabIsFixed
+ * Use dynamic PLAB or fixed size PLAB
+ * @param objectsAreReachable
+ * true - allocate live objects
+ * false - allocate unreachable objects
+ * @param promotedByPLAB
+ * true - we expect to see PLAB allocation during promotion
+ * false - objects will be directly allocated during promotion
+ */
+ public TestCase(int wastePct,
+ int plabSize,
+ int chunkSize,
+ int parGCThreads,
+ int edenSize,
+ boolean plabIsFixed,
+ boolean objectsAreReachable,
+ boolean promotedByPLAB
+ ) {
+ if (wastePct == 0 || plabSize == 0 || chunkSize == 0 || parGCThreads == 0 || edenSize == 0) {
+ throw new IllegalArgumentException("Parameters should not be 0");
+ }
+ this.wastePct = wastePct;
+ this.plabSize = plabSize;
+ this.chunkSize = chunkSize;
+ this.parGCThreads = parGCThreads;
+ this.edenSize = edenSize;
+ this.plabIsFixed = plabIsFixed;
+ this.objectsAreReachable = objectsAreReachable;
+ this.promotedByPLAB = promotedByPLAB;
+ }
+
+ /**
+ * Convert current TestCase to List of options.
+ * Assume test will fill half of existed eden.
+ *
+ * @return
+ * List of options
+ */
+ public List<String> toOptions() {
+ return Arrays.asList("-XX:ParallelGCThreads=" + parGCThreads,
+ "-XX:ParallelGCBufferWastePct=" + wastePct,
+ "-XX:OldPLABSize=" + plabSize,
+ "-XX:YoungPLABSize=" + plabSize,
+ "-XX:" + (plabIsFixed ? "-" : "+") + "ResizePLAB",
+ "-Dchunk.size=" + chunkSize,
+ "-Dreachable=" + objectsAreReachable,
+ "-XX:NewSize=" + edenSize + "m",
+ "-XX:MaxNewSize=" + edenSize + "m",
+ "-Dmem.to.fill=" + getMemToFill()
+ );
+ }
+
+ /**
+ * Print details about test case.
+ */
+ public void print(PrintStream out) {
+ boolean expectPLABAllocation = promotedByPLAB && objectsAreReachable;
+ boolean expectDirectAllocation = (!promotedByPLAB) && objectsAreReachable;
+
+ out.println("Test case details:");
+ out.println(" Young gen size : " + edenSize + "M");
+ out.println(" Predefined PLAB size : " + plabSize);
+ out.println(" Parallel GC buffer waste pct : " + wastePct);
+ out.println(" Chunk size : " + chunkSize);
+ out.println(" Parallel GC threads : " + parGCThreads);
+ out.println(" Objects are created : " + (objectsAreReachable ? "reachable" : "unreachable"));
+ out.println(" PLAB size is fixed: " + (plabIsFixed ? "yes" : "no"));
+ out.println("Test expectations:");
+ out.println(" PLAB allocation : " + (expectPLABAllocation ? "expected" : "unexpected"));
+ out.println(" Direct allocation : " + (expectDirectAllocation ? "expected" : "unexpected"));
+ }
+
+ /**
+ * @return
+ * true if we expect PLAB allocation
+ * false if no
+ */
+ public boolean isPromotedByPLAB() {
+ return promotedByPLAB;
+ }
+
+ /**
+ * @return
+ * true if it is test case for unreachable objects
+ * false for live objects
+ */
+ public boolean isDeadObjectCase() {
+ return !objectsAreReachable;
+ }
+
+ /**
+ * Returns amount of memory to fill
+ *
+ * @return amount of memory
+ */
+ public long getMemToFill() {
+ return (long) (edenSize) * 1024l * 1024l / 2;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/g1/plab/TestPLABResize.java Wed Jan 27 18:22:25 2016 +0300
@@ -0,0 +1,222 @@
+/*
+ * 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 TestPLABResize
+ * @bug 8141278
+ * @summary Test for PLAB resizing
+ * @requires vm.gc=="G1" | vm.gc=="null"
+ * @library /testlibrary /../../test/lib /
+ * @modules java.management
+ * @build ClassFileInstaller
+ * sun.hotspot.WhiteBox
+ * gc.g1.plab.lib.LogParser
+ * gc.g1.plab.lib.MemoryConsumer
+ * gc.g1.plab.lib.PLABUtils
+ * gc.g1.plab.lib.AppPLABResize
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main gc.g1.plab.TestPLABResize
+ */
+package gc.g1.plab;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.io.PrintStream;
+
+import gc.g1.plab.lib.LogParser;
+import gc.g1.plab.lib.PLABUtils;
+import gc.g1.plab.lib.AppPLABResize;
+
+import jdk.test.lib.OutputAnalyzer;
+import jdk.test.lib.ProcessTools;
+
+/**
+ * Test for PLAB resizing.
+ */
+public class TestPLABResize {
+
+ private static final int OBJECT_SIZE_SMALL = 10;
+ private static final int OBJECT_SIZE_MEDIUM = 70;
+ private static final int OBJECT_SIZE_HIGH = 150;
+ private static final int GC_NUM_SMALL = 1;
+ private static final int GC_NUM_MEDIUM = 3;
+ private static final int GC_NUM_HIGH = 7;
+ private static final int WASTE_PCT_SMALL = 10;
+ private static final int WASTE_PCT_MEDIUM = 20;
+ private static final int WASTE_PCT_HIGH = 30;
+
+ private static final int ITERATIONS_SMALL = 3;
+ private static final int ITERATIONS_MEDIUM = 5;
+ private static final int ITERATIONS_HIGH = 8;
+
+ private final static TestCase[] TEST_CASES = {
+ new TestCase(WASTE_PCT_SMALL, OBJECT_SIZE_SMALL, GC_NUM_SMALL, ITERATIONS_MEDIUM),
+ new TestCase(WASTE_PCT_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_HIGH, ITERATIONS_SMALL),
+ new TestCase(WASTE_PCT_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, ITERATIONS_HIGH),
+ new TestCase(WASTE_PCT_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, ITERATIONS_MEDIUM),
+ new TestCase(WASTE_PCT_MEDIUM, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, ITERATIONS_SMALL),
+ new TestCase(WASTE_PCT_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, ITERATIONS_HIGH),
+ new TestCase(WASTE_PCT_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, ITERATIONS_MEDIUM),
+ new TestCase(WASTE_PCT_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, ITERATIONS_SMALL),
+ new TestCase(WASTE_PCT_HIGH, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, ITERATIONS_HIGH)
+ };
+
+ public static void main(String[] args) throws Throwable {
+ for (TestCase testCase : TEST_CASES) {
+ testCase.print(System.out);
+ List<String> options = PLABUtils.prepareOptions(testCase.toOptions());
+ options.add(AppPLABResize.class.getName());
+ OutputAnalyzer out = ProcessTools.executeTestJvm(options.toArray(new String[options.size()]));
+ if (out.getExitValue() != 0) {
+ System.out.println(out.getOutput());
+ throw new RuntimeException("Exit code is not 0");
+ }
+ checkResults(out.getOutput(), testCase);
+ }
+ }
+
+ /**
+ * Checks testing results.
+ * Expected results - desired PLAB size is decreased and increased during promotion to Survivor.
+ *
+ * @param output - VM output
+ * @param testCase
+ */
+ private static void checkResults(String output, TestCase testCase) {
+ final LogParser log = new LogParser(output);
+ final Map<Long, Map<LogParser.ReportType, Map<String, Long>>> entries = log.getEntries();
+
+ final ArrayList<Long> plabSizes = entries.entrySet()
+ .stream()
+ .map(item -> {
+ return item.getValue()
+ .get(LogParser.ReportType.SURVIVOR_STATS)
+ .get("desired_plab_sz");
+ })
+ .collect(Collectors.toCollection(ArrayList::new));
+
+ // Check that desired plab size was changed during iterations.
+ // It should decrease during first half of iterations
+ // and increase after.
+ List<Long> decreasedPlabs = plabSizes.subList(testCase.getIterations(), testCase.getIterations() * 2);
+ List<Long> increasedPlabs = plabSizes.subList(testCase.getIterations() * 2, testCase.getIterations() * 3);
+
+ Long prev = decreasedPlabs.get(0);
+ for (int index = 1; index < decreasedPlabs.size(); ++index) {
+ Long current = decreasedPlabs.get(index);
+ if (prev < current) {
+ System.out.println(output);
+ throw new RuntimeException("Test failed! Expect that previous PLAB size should be greater than current. Prev.size: " + prev + " Current size:" + current);
+ }
+ prev = current;
+ }
+
+ prev = increasedPlabs.get(0);
+ for (int index = 1; index < increasedPlabs.size(); ++index) {
+ Long current = increasedPlabs.get(index);
+ if (prev > current) {
+ System.out.println(output);
+ throw new RuntimeException("Test failed! Expect that previous PLAB size should be less than current. Prev.size: " + prev + " Current size:" + current);
+ }
+ prev = current;
+ }
+
+ System.out.println("Test passed!");
+ }
+
+ /**
+ * Description of one test case.
+ */
+ private static class TestCase {
+
+ private final int wastePct;
+ private final int chunkSize;
+ private final int parGCThreads;
+ private final int iterations;
+
+ /**
+ * @param wastePct
+ * ParallelGCBufferWastePct
+ * @param chunkSize
+ * requested object size for memory consumption
+ * @param parGCThreads
+ * -XX:ParallelGCThreads
+ * @param iterations
+ *
+ */
+ public TestCase(int wastePct,
+ int chunkSize,
+ int parGCThreads,
+ int iterations
+ ) {
+ if (wastePct == 0 || chunkSize == 0 || parGCThreads == 0 || iterations == 0) {
+ throw new IllegalArgumentException("Parameters should not be 0");
+ }
+ this.wastePct = wastePct;
+
+ this.chunkSize = chunkSize;
+ this.parGCThreads = parGCThreads;
+ this.iterations = iterations;
+ }
+
+ /**
+ * Convert current TestCase to List of options.
+ *
+ * @return
+ * List of options
+ */
+ public List<String> toOptions() {
+ return Arrays.asList("-XX:ParallelGCThreads=" + parGCThreads,
+ "-XX:ParallelGCBufferWastePct=" + wastePct,
+ "-XX:+ResizePLAB",
+ "-Dthreads=" + parGCThreads,
+ "-Dchunk.size=" + chunkSize,
+ "-Diterations=" + iterations,
+ "-XX:NewSize=16m",
+ "-XX:MaxNewSize=16m"
+ );
+ }
+
+ /**
+ * Print details about test case.
+ */
+ public void print(PrintStream out) {
+ out.println("Test case details:");
+ out.println(" Parallel GC buffer waste pct : " + wastePct);
+ out.println(" Chunk size : " + chunkSize);
+ out.println(" Parallel GC threads : " + parGCThreads);
+ out.println(" Iterations: " + iterations);
+ }
+
+ /**
+ * @return iterations
+ */
+ public int getIterations() {
+ return iterations;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/g1/plab/lib/AppPLABPromotion.java Wed Jan 27 18:22:25 2016 +0300
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+package gc.g1.plab.lib;
+
+import sun.hotspot.WhiteBox;
+
+/**
+ * This application is part of PLAB promotion test.
+ * The application fills a part of young gen with a number of small objects.
+ * Then it calls young GC twice to promote objects from eden to survivor, and from survivor to old.
+ * The test which running the application is responsible to set up test parameters
+ * and VM flags including flags turning GC logging on. The test will then check the produced log.
+ */
+final public class AppPLABPromotion {
+
+ private final static WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+ /**
+ * AppPLABPromotion is used for testing PLAB promotion.
+ * Expects the following properties to be set:
+ * - chunk.size - size of one object (byte array)
+ * - mem.to.fill - amount of memory to be consumed
+ * - reachable - memory should be consumed by live or dead objects
+ *
+ * @param args
+ */
+ public static void main(String[] args) {
+ long chunkSize = Long.getLong("chunk.size");
+ long memToFill = Long.getLong("mem.to.fill");
+ boolean reachable = Boolean.getBoolean("reachable");
+ if (chunkSize == 0) {
+ throw new IllegalArgumentException("Chunk size must be not 0");
+ }
+ if (memToFill <= 0) {
+ throw new IllegalArgumentException("mem.to.fill property should be above 0");
+ }
+ // Fill requested amount of memory
+ allocate(reachable, memToFill, chunkSize);
+ // Promote all allocated objects from Eden to Survivor
+ WHITE_BOX.youngGC();
+ // Promote all allocated objects from Survivor to Old
+ WHITE_BOX.youngGC();
+ }
+
+ /**
+ *
+ * @param reachable - should allocate reachable object
+ * @param memSize - Memory to fill
+ * @param chunkSize - requested bytes per objects.
+ * Actual size of bytes per object will be greater
+ */
+ private static void allocate(boolean reachable, long memSize, long chunkSize) {
+ long realSize = WHITE_BOX.getObjectSize(new byte[(int) chunkSize]);
+ int items = (int) ((memSize - 1) / (realSize)) + 1;
+ MemoryConsumer storage;
+ if (reachable) {
+ storage = new MemoryConsumer(items, (int) chunkSize);
+ } else {
+ storage = new MemoryConsumer(1, (int) chunkSize);
+ }
+ // Make all young gen available.
+ WHITE_BOX.fullGC();
+ storage.consume(items * chunkSize);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/g1/plab/lib/AppPLABResize.java Wed Jan 27 18:22:25 2016 +0300
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+package gc.g1.plab.lib;
+
+import jdk.test.lib.Platform;
+import sun.hotspot.WhiteBox;
+
+/**
+ * This application is part of PLAB Resize test.
+ * The application allocates objects in 3 iterations:
+ * 1. Objects of fixed size
+ * 2. Objects of decreasing size
+ * 3. Objects of increasing size
+ * The application doesn't have any assumptions about expected behavior.
+ * It's supposed to be executed by a test which should set up test parameters (object sizes, number of allocations, etc)
+ * and VM flags including flags turning GC logging on. The test will then check the produced log.
+ *
+ * Expects the following properties to be set:
+ * - iterations - amount of iteration per cycle.
+ * - chunk.size - size of objects to be allocated
+ * - threads - number of gc threads (-XX:ParallelGCThreads) to calculate PLAB sizes.
+ */
+final public class AppPLABResize {
+
+ // Memory to be promoted by PLAB for one thread.
+ private static final long MEM_ALLOC_WORDS = 32768;
+ // Defined by properties.
+ private static final int ITERATIONS = Integer.getInteger("iterations");
+ private static final long CHUNK = Long.getLong("chunk.size");
+ private static final int GC_THREADS = Integer.getInteger("threads");
+
+ private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+ /**
+ * Main method for AppPLABResizing. Application expect for next properties:
+ * iterations, chunk.size and threads.
+ *
+ * @param args
+ */
+ public static void main(String[] args) {
+
+ if (ITERATIONS == 0 || CHUNK == 0 || GC_THREADS == 0) {
+ throw new IllegalArgumentException("Properties should be set");
+ }
+
+ long wordSize = Platform.is32bit() ? 4l : 8l;
+ // PLAB size is shared between threads.
+ long initialMemorySize = wordSize * GC_THREADS * MEM_ALLOC_WORDS;
+
+ // Expect changing memory to half during all iterations.
+ long memChangeStep = initialMemorySize / 2 / ITERATIONS;
+
+ WHITE_BOX.fullGC();
+
+ // Warm the PLAB. Fill memory ITERATIONS times without changing memory size.
+ iterateAllocation(initialMemorySize, 0);
+ // Fill memory ITERATIONS times.
+ // Initial size is initialMemorySize and step is -memChangeStep
+ iterateAllocation(initialMemorySize, -memChangeStep);
+ // Fill memory ITERATIONS times.
+ // Initial size is memoryAfterChanging, step is memChangeStep.
+ // Memory size at start should be greater then last size on previous step.
+ // Last size on previous step is initialMemorySize - memChangeStep*(ITERATIONS - 1)
+ long memoryAfterChanging = initialMemorySize - memChangeStep * (ITERATIONS - 2);
+ iterateAllocation(memoryAfterChanging, memChangeStep);
+ }
+
+ private static void iterateAllocation(long memoryToFill, long change) {
+ int items;
+ if (change > 0) {
+ items = (int) ((memoryToFill + change * ITERATIONS) / CHUNK) + 1;
+ } else {
+ items = (int) (memoryToFill / CHUNK) + 1;
+ }
+
+ long currentMemToFill = memoryToFill;
+ for (int iteration = 0; iteration < ITERATIONS; ++iteration) {
+ MemoryConsumer storage = new MemoryConsumer(items, (int) CHUNK);
+ storage.consume(currentMemToFill);
+ // Promote all objects to survivor
+ WHITE_BOX.youngGC();
+ storage.clear();
+ currentMemToFill += change;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/g1/plab/lib/LogParser.java Wed Jan 27 18:22:25 2016 +0300
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+package gc.g1.plab.lib;
+
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Scanner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * LogParser class parses VM output to get PLAB and ConsumptionStats values.
+ *
+ * Typical GC log with PLAB statistics (options - -Xlog:gc=debug,gc+plab=debug) looks like:
+ *
+ * [2,244s][info ][gc ] GC(30) Concurrent Mark abort
+ * [2,245s][debug ][gc,plab] GC(33) (allocated = 1 wasted = 0 unused = 0 used = 1 undo_waste = 0 region_end_waste = 0 regions filled = 0 direct_allocated = 0 failure_used = 0 failure_waste = 0) (plab_sz = 0 desired_plab_sz = 258)
+ * [2,245s][debug ][gc,plab] GC(33) (allocated = 1 wasted = 0 unused = 0 used = 1 undo_waste = 0 region_end_waste = 0 regions filled = 0 direct_allocated = 0 failure_used = 0 failure_waste = 0) (plab_sz = 0 desired_plab_sz = 258)
+ * [2,245s][info ][gc ] GC(33) Pause Young (G1 Evacuation Pause) 127M->127M(128M) (2,244s, 2,245s) 0,899ms
+ * [2,246s][debug ][gc,plab] GC(34) (allocated = 1 wasted = 0 unused = 0 used = 1 undo_waste = 0 region_end_waste = 0 regions filled = 0 direct_allocated = 0 failure_used = 0 failure_waste = 0) (plab_sz = 0 desired_plab_sz = 258)
+ * [2,246s][debug ][gc,plab] GC(34) (allocated = 1 wasted = 0 unused = 0 used = 1 undo_waste = 0 region_end_waste = 0 regions filled = 0 direct_allocated = 0 failure_used = 0 failure_waste = 0) (plab_sz = 0 desired_plab_sz = 258)
+ * [2,246s][info ][gc ] GC(34) Pause Initial Mark (G1 Evacuation Pause) 127M->127M(128M) (2,245s, 2,246s) 0,907ms
+
+ */
+final public class LogParser {
+
+ // Name for GC ID field in report.
+ public final static String GC_ID = "gc_id";
+
+ /**
+ * Type of parsed log element.
+ */
+ public static enum ReportType {
+
+ SURVIVOR_STATS,
+ OLD_STATS
+ }
+
+ private final String log;
+
+ private final Map<Long, Map<ReportType, Map<String,Long>>> reportHolder;
+
+ // GC ID
+ private static final Pattern GC_ID_PATTERN = Pattern.compile("\\[gc,plab\\s*\\] GC\\((\\d+)\\)");
+ // Pattern for extraction pair <name>=<numeric value>
+ private static final Pattern PAIRS_PATTERN = Pattern.compile("\\w+\\s+=\\s+\\d+");
+
+ /**
+ * Construct LogParser Object
+ *
+ * @param log - VM Output
+ */
+ public LogParser(String log) {
+ if (log == null) {
+ throw new IllegalArgumentException("Parameter log should not be null.");
+ }
+ this.log = log;
+ reportHolder = parseLines();
+ }
+
+ /**
+ * @return log which is being processed
+ */
+ public String getLog() {
+ return log;
+ }
+
+ /**
+ * Returns list of log entries.
+ *
+ * @return list of Pair with ReportType and Map of parameters/values.
+ */
+ public Map<Long,Map<ReportType, Map<String,Long>>> getEntries() {
+ return reportHolder;
+ }
+
+ private Map<Long,Map<ReportType, Map<String,Long>>> parseLines() throws NumberFormatException {
+ Scanner lineScanner = new Scanner(log);
+ Map<Long,Map<ReportType, Map<String,Long>>> allocationStatistics = new HashMap<>();
+ Optional<Long> gc_id;
+ while (lineScanner.hasNextLine()) {
+ String line = lineScanner.nextLine();
+ gc_id = getGcId(line);
+ if ( gc_id.isPresent() ) {
+ Matcher matcher = PAIRS_PATTERN.matcher(line);
+ if (matcher.find()) {
+ Map<ReportType,Map<String, Long>> oneReportItem;
+ ReportType reportType;
+ // Second line in log is statistics for Old PLAB allocation
+ if ( !allocationStatistics.containsKey(gc_id.get()) ) {
+ oneReportItem = new EnumMap<>(ReportType.class);
+ reportType = ReportType.SURVIVOR_STATS;
+ allocationStatistics.put(gc_id.get(), oneReportItem);
+ } else {
+ oneReportItem = allocationStatistics.get(gc_id.get());
+ reportType = ReportType.OLD_STATS;
+ }
+
+ // Extract all pairs from log.
+ HashMap<String, Long> plabStats = new HashMap<>();
+ do {
+ String pair = matcher.group();
+ String[] nameValue = pair.replaceAll(" ", "").split("=");
+ plabStats.put(nameValue[0], Long.parseLong(nameValue[1]));
+ } while (matcher.find());
+ oneReportItem.put(reportType,plabStats);
+ }
+ }
+ }
+ return allocationStatistics;
+ }
+
+ private Optional<Long> getGcId(String line) {
+ Matcher number = GC_ID_PATTERN.matcher(line);
+ if (number.find()) {
+ return Optional.of(Long.parseLong(number.group(1)));
+ }
+ return Optional.empty();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/g1/plab/lib/MemoryConsumer.java Wed Jan 27 18:22:25 2016 +0300
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+package gc.g1.plab.lib;
+
+/**
+ * The MemoryConsumer is used for consuming different amount of memory.
+ * Class will store not more than 'capacity' number of objects with 'chunk' size.
+ * If we exceed capacity, object will be stored at existing entries,
+ * all previously added objects will be overwritten.
+ * If capacity=1, only last object will be saved.
+ */
+public class MemoryConsumer {
+
+ private int capacity;
+ private int chunk;
+
+ private Object[] array;
+ private int index;
+
+ /**
+ * Create MemoryConsumer object with defined capacity
+ *
+ * @param capacity
+ * @param chunk
+ */
+ public MemoryConsumer(int capacity, int chunk) {
+ if (capacity <= 0) {
+ throw new IllegalArgumentException("Items number should be greater than 0.");
+ }
+ if (chunk <= 0) {
+ throw new IllegalArgumentException("Chunk size should be greater than 0.");
+ }
+ this.capacity = capacity;
+ this.chunk = chunk;
+ index = 0;
+ array = new Object[this.capacity];
+ }
+
+ /**
+ * Store object into MemoryConsumer.
+ *
+ * @param o - Object to store
+ */
+ private void store(Object o) {
+ if (array == null) {
+ throw new RuntimeException("Capacity should be set before storing");
+ }
+ array[index % capacity] = o;
+ ++index;
+ }
+
+ public void consume(long memoryToFill) {
+ long allocated = 0;
+ while (allocated < memoryToFill) {
+ store(new byte[chunk]);
+ allocated += chunk;
+ }
+ }
+
+ /**
+ * Clear all stored objects.
+ */
+ public void clear() {
+ array = null;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/g1/plab/lib/PLABUtils.java Wed Jan 27 18:22:25 2016 +0300
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+package gc.g1.plab.lib;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import jdk.test.lib.Utils;
+
+/**
+ * Utilities for PLAB testing.
+ */
+public class PLABUtils {
+
+ /**
+ * PLAB tests default options list
+ */
+ private final static String[] GC_TUNE_OPTIONS = {
+ "-XX:+UseG1GC",
+ "-XX:G1HeapRegionSize=1m",
+ "-XX:OldSize=64m",
+ "-XX:-UseAdaptiveSizePolicy",
+ "-XX:-UseTLAB",
+ "-XX:SurvivorRatio=1"
+ };
+
+ /**
+ * GC logging options list.
+ */
+ private final static String G1_PLAB_LOGGING_OPTIONS[] = {
+ "-Xlog:gc=debug,gc+plab=debug"
+ };
+
+ /**
+ * List of options required to use WhiteBox.
+ */
+ private final static String WB_DIAGNOSTIC_OPTIONS[] = {
+ "-Xbootclasspath/a:.",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI"
+ };
+
+ /**
+ * Prepares options for testing.
+ *
+ * @param options - additional options for testing
+ * @return List of options
+ */
+ public static List<String> prepareOptions(List<String> options) {
+ if (options == null) {
+ throw new IllegalArgumentException("Options cannot be null");
+ }
+ List<String> executionOtions = new ArrayList<>(
+ Arrays.asList(Utils.getTestJavaOpts())
+ );
+ Collections.addAll(executionOtions, WB_DIAGNOSTIC_OPTIONS);
+ Collections.addAll(executionOtions, G1_PLAB_LOGGING_OPTIONS);
+ Collections.addAll(executionOtions, GC_TUNE_OPTIONS);
+ executionOtions.addAll(options);
+ return executionOtions;
+ }
+}