--- /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; + } + } +}