This commit is contained in:
Igor Ignatyev 2016-01-29 12:30:00 +00:00
commit 2c54bcb248
7 changed files with 1051 additions and 0 deletions

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}