--- /dev/null 2015-11-11 00:08:33.433730035 +0300 +++ new/test/gc/g1/plab/TestPLABNoResize.java 2015-11-17 19:17:55.160851412 +0300 @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2015, 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 TestPLABNoResize + * @bug 8141278 + * @summary Test for PLAB allocation in Survivor and Old + * @requires vm.gc=="G1" | vm.gc=="null" + * @library /testlibrary /../../test/lib / + * @modules java.management + * @build ClassFileInstaller + * sun.hotspot.WhiteBox + * gc.g1.plab.lib.Storage + * gc.g1.plab.lib.LogParser + * gc.g1.plab.lib.AppPLABFixedSize + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main gc.g1.plab.TestPLABNoResize + */ +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.AppPLABFixedSize; +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 for fixed size PLAB. + */ +public class TestPLABNoResize { + + // 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; + + // Some threshold. We do not expect actual size of promoted with PLAB, + // just check that 'dead objects were not promoted','small objects were promoted' and + // 'large object were not promoted'. + 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 final static TestCase[] TEST_CASES = { + // Test cases for unreachable object + new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, false, false), + new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, false, false), + // Test cases for reachable objects + new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, true, true), + new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_MEDIUM, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, true, true), + new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, true, false), + new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, true, true), + new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, true, true), + new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, true, true), + new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_SMALL, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, true, true), + new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, true, true), + new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_HIGH, true, false) + }; + + private static final String[] PLAB_OPTIONS = { + "-XX:-ResizePLAB" + }; + + public static void main(String[] args) throws Throwable { + + for (TestCase testCase : TEST_CASES) { + // What we going to check. + testCase.print(System.out); + List options = PLABUtils.prepareOptions(PLAB_OPTIONS); + options.addAll(testCase.toOptions()); + options.add(AppPLABFixedSize.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 survivorStats = getPlabStats(logParser, LogParser.ReportType.SURVIVOR_STATS, GC_ID_SURVIVOR_STATS); + Map 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", plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated); + + // Unreachable object case + if (testCase.isDeadObjectCase()) { + // All dead object should not 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 { + // All live small objects should be promoted using PLAB + if (testCase.isPromotedByPLAB() && 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 (testCase.isPromotedByPLAB() && 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"); + } + + // All big objects should be directly allocated + if (!testCase.isPromotedByPLAB() && 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 (!testCase.isPromotedByPLAB() && 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"); + } + } + System.out.println("Test passed!"); + } + + private static Map getPlabStats(LogParser logParser, LogParser.ReportType type, long gc_id) { + Map survivorStats = logParser.getEntries() + .stream() + .filter(p -> p.first == type) + .map(p -> p.second) + .filter(l -> l.get(LogParser.GC_ID) == gc_id) + .findFirst() + .orElseThrow(() -> { + System.out.println(logParser.getLog()); + return new RuntimeException("Cannot find requested GC_ID - " + gc_id); + }); + 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 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 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 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.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 toOptions() { + return Arrays.asList("-XX:ParallelGCThreads=" + parGCThreads, + "-XX:ParallelGCBufferWastePct=" + wastePct, + "-XX:OldPLABSize=" + plabSize, + "-XX:YoungPLABSize=" + plabSize, + "-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("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 2015-11-11 00:08:33.433730035 +0300 +++ new/test/gc/g1/plab/TestPLABResize.java 2015-11-17 19:17:55.476856205 +0300 @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2015, 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.Storage + * 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.Pair; +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) + }; + + private static final String[] PLAB_OPTIONS = { + "-XX:+ResizePLAB" + }; + + public static void main(String[] args) throws Throwable { + for (TestCase testCase : TEST_CASES) { + testCase.print(System.out); + List options = PLABUtils.prepareOptions(PLAB_OPTIONS); + options.addAll(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 List>> entries = log.getEntries(); + final ArrayList plabSizes = entries.stream() + .filter(p -> p.first == LogParser.ReportType.SURVIVOR_STATS) + .map(p -> p.second) + // We do not need first iterations - it is 'warm' phase of allocation. + .filter(mapEntries -> mapEntries.get(LogParser.GC_ID) > testCase.iterations) + .map(items -> items.get("desired_plab_sz")) + .collect(Collectors.toCollection(ArrayList::new)); + + if (plabSizes.isEmpty()) { + System.out.println(output); + throw new RuntimeException("Cannot find desired_plab_sz information for gc_id>" + testCase.iterations); + } + + Long prev = plabSizes.get(0); + // Check that desired plab size is changed during iterations. + // It should decrease during first half of iterations + // and increase after. + for (int index = 1; index < plabSizes.size(); ++index) { + Long current = plabSizes.get(index); + if (index < testCase.getIterations()) { + if (prev < current) { + System.out.println(output); + throw new RuntimeException("Test failed! Expect that previout PLAB size should be less than current. Prev.size: " + prev + " Current size:" + current); + } + } else if (prev > current) { + System.out.println(output); + throw new RuntimeException("Test failed! Expect that previout PLAB size should be greater 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 toOptions() { + return Arrays.asList("-XX:ParallelGCThreads=" + parGCThreads, + "-XX:ParallelGCBufferWastePct=" + wastePct, + "-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 per one allocation part : " + iterations); + out.println(" All iteration of allocations : " + iterations * 3); + } + + /** + * @return iterations + */ + public int getIterations() { + return iterations; + } + } +} --- /dev/null 2015-11-11 00:08:33.433730035 +0300 +++ new/test/gc/g1/plab/lib/AppPLABFixedSize.java 2015-11-17 19:17:55.796861060 +0300 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015, 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; + +/** + * A simple application a part of PLAB no resize test. + * The application fills a part of young get up with a number of small objects. + * Then it calls young GC twice to promote objects from eden to survivor, and from survivor to old. + * Then it prints out the statistics on allocated objections. + * The test running the application is responsible to setup 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. + */ +final public class AppPLABFixedSize { + + private final static WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + /** + * AppPLABFixedSize for testing fixed size PLAB. + * Expects next properties: chunk.size, allocator.type, reachable + * + * @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"); + } + // 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; + Storage storage; + if (reachable) { + storage = new Storage(items); + } else { + storage = new Storage(1); + } + // Make all young gen available. + WHITE_BOX.fullGC(); + long allocated = 0; + // Expect no GC during allocation. + while (allocated < memSize) { + storage.store(new byte[(int) chunkSize]); + allocated += realSize; + } + } +} --- /dev/null 2015-11-11 00:08:33.433730035 +0300 +++ new/test/gc/g1/plab/lib/AppPLABResize.java 2015-11-17 19:17:56.116865914 +0300 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015, 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; + +/** + * An simple application a part of PLAB Resize test to be executed in the VM under tests. + * 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 a test which suppose to setup 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. + */ +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 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 || THREADS == 0) { + throw new IllegalArgumentException("Properties should be set"); + } + + long wordSize = Platform.is32bit() ? 4l : 8l; + // PLAB size is shared between threads. + long initialMemorySize = wordSize * 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) { + + Storage storage = new Storage(items); + allocateYoung(storage, currentMemToFill); + // Promote all objects to survivor + WHITE_BOX.youngGC(); + storage.clear(); + currentMemToFill += change; + } + } + + private static void allocateYoung(Storage storage, long memoryToFill) { + long allocated = 0; + while (allocated < memoryToFill) { + storage.store(new byte[(int) CHUNK]); + allocated += CHUNK; + } + } +} --- /dev/null 2015-11-11 00:08:33.433730035 +0300 +++ new/test/gc/g1/plab/lib/LogParser.java 2015-11-17 19:17:56.436870768 +0300 @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015, 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.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import jdk.test.lib.Pair; + +/** + * LogParser class parses VM output to get PLAB and ConsumptionStats values. + * + * Typical GC log with PLAB statistics looks like: + * + * #1: [GC pause (WhiteBox Initiated Young GC) (young) (allocated = 0 wasted = 0 unused = 0 used = 0 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) + * (allocated = 70692 wasted = 1450 unused = 1287 used = 67955 undo_waste = 0 region_end_waste = 0 regions filled = 1 direct_allocated = 19003 failure_used = 0 failure_waste = 0) (plab_sz = 13591 desired_plab_sz = 10193) + * where first line is statistic for young PLAB and second line is for old PLAB. + */ +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 List>> reportHolder; + + // GC ID + private static final Pattern GC_ID_PATTERN = Pattern.compile("#(\\d+):"); + // Pattern for extraction pair = + 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 List>> getEntries() { + return reportHolder; + } + + private List>> parseLines() throws NumberFormatException { + Scanner lineScanner = new Scanner(log); + List>> allocationStatistics = new ArrayList<>(); + long gc_id = 0; + while (lineScanner.hasNextLine()) { + String line = lineScanner.nextLine(); + // If no any prefix - this is the Old PLAB statistics + ReportType currentStats = ReportType.OLD_STATS; + // #NUMBER - this is first line of PLAB statistics + if (line.startsWith("#")) { + currentStats = ReportType.SURVIVOR_STATS; + gc_id = getGcId(line); + } + + Matcher matcher = PAIRS_PATTERN.matcher(line); + if (matcher.find()) { + HashMap map = new HashMap<>(); + map.put(GC_ID, gc_id); + allocationStatistics.add(new Pair<>(currentStats, map)); + // Extract all pairs from log. + do { + String pair = matcher.group(); + String[] nameValue = pair.replaceAll(" ", "").split("="); + map.put(nameValue[0], Long.parseLong(nameValue[1])); + } while (matcher.find()); + } + } + return allocationStatistics; + } + + private long getGcId(String line) { + Matcher number = GC_ID_PATTERN.matcher(line); + if (number.find()) { + return Long.parseLong(number.group(1)); + } + throw new RuntimeException("Should not reach"); + } +} --- /dev/null 2015-11-11 00:08:33.433730035 +0300 +++ new/test/gc/g1/plab/lib/PLABUtils.java 2015-11-17 19:17:56.756875621 +0300 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2015, 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 { + + /** + * List of VM options for GC tuning. + */ + private final static String[] GC_TUNE_OPTIONS = { + "-XX:+UseG1GC", + "-XX:OldSize=64m", + "-XX:-UseAdaptiveSizePolicy", + "-XX:-UseTLAB", + "-XX:SurvivorRatio=1" + }; + + /** + * List of options for GC logging. + */ + private final static String G1_PLAB_LOGGING_OPTIONS[] = { + "-XX:+PrintPLAB", + "-XX:+PrintGC" + }; + + /** + * List of options for WhiteBox using. + */ + 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 prepareOptions(String... options) { + if (options == null) { + throw new IllegalArgumentException("Options cannot be null"); + } + List 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); + Collections.addAll(executionOtions, options); + return executionOtions; + } +} --- /dev/null 2015-11-11 00:08:33.433730035 +0300 +++ new/test/gc/g1/plab/lib/Storage.java 2015-11-17 19:17:57.080880537 +0300 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015, 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 Storage is used for storing reachable objects. + * Class will store not more than capacity, which can be set during creation. + * If we exceed capacity, object will be stored at existing entries. + * So, if capacity=1, all previously added objects will be unreachable. + */ +public class Storage { + + private int capacity; + + private Object[] array; + private int index; + + /** + * Create Storage object with defined capacity + * + * @param capacity + */ + public Storage(int capacity) { + if (capacity <= 0) { + throw new IllegalArgumentException("Items number should be greater than 0."); + } + this.capacity = capacity; + index = 0; + array = new Object[this.capacity]; + } + + /** + * Store object into Storage. + * + * @param o - Object to store + */ + public void store(Object o) { + if (array == null) { + throw new RuntimeException("Capacity should be set before storing"); + } + array[index % capacity] = o; + ++index; + } + + /** + * Clear all stored objects. + */ + public void clear() { + array = null; + } +}