--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/stress/gc/TestStressRSetCoarsening.java Fri Jan 29 16:17:05 2016 +0400
@@ -0,0 +1,335 @@
+/*
+ * 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.
+ */
+
+import java.util.concurrent.TimeoutException;
+import sun.hotspot.WhiteBox;
+
+/*
+ * @test TestStressRSetCoarsening.java
+ * @key stress
+ * @bug 8146984 8147087
+ * @requires vm.gc=="G1" | vm.gc=="null"
+ * @requires os.maxMemory > 3G
+ *
+ * @summary Stress G1 Remembered Set by creating a lot of cross region links
+ * @modules java.base/sun.misc
+ * @library /testlibrary /test/lib
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm/timeout=300
+ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
+ * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc
+ * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 1 0 300
+ * @run main/othervm/timeout=300
+ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
+ * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc
+ * -Xmx500m -XX:G1HeapRegionSize=8m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 1 10 300
+ * @run main/othervm/timeout=300
+ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
+ * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc
+ * -Xmx500m -XX:G1HeapRegionSize=32m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 42 10 300
+ * @run main/othervm/timeout=300
+ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
+ * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc
+ * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 2 0 300
+ * @run main/othervm/timeout=1800
+ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
+ * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc
+ * -Xmx1G -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 500 0 1800
+ * @run main/othervm/timeout=1800
+ * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC
+ * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc
+ * -Xmx1G -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 10 10 1800
+ */
+
+/**
+ * What the test does.
+ * Preparation stage:
+ * Fill out ~90% of the heap with objects, each object is an object array.
+ * If we want to allocate K objects per region, we calculate N to meet:
+ * sizeOf(Object[N]) ~= regionSize / K
+ * Stress stage:
+ * No more allocation, so no more GC.
+ * We will perform a number of iterations. On each iteration i,
+ * for each pair of regions Rx and Ry we will set c[i] references
+ * from Rx to Ry. If c[i] less than c[i-1] at the end of iteration
+ * concurrent mark cycle will be initiated (to recalculate remembered sets).
+ * As the result RSet will be growing up and down, up and down many times.
+ *
+ * The test expects: no crash and no timeouts.
+ *
+ * Test Parameters:
+ * args[0] - number of objects per Heap Region (1 - means humongous)
+ * args[1] - number of regions to refresh to provoke GC at the end of cycle.
+ * (0 - means no GC, i.e. no reading from RSet)
+ * args[2] - timeout in seconds (to stop execution to avoid jtreg timeout)
+ */
+public class TestStressRSetCoarsening {
+
+ public static void main(String... args) throws InterruptedException {
+ if (args.length != 3) {
+ throw new IllegalArgumentException("Wrong number of arguments " + args.length);
+ }
+ int objectsPerRegion = Integer.parseInt(args[0]); // 1 means humongous
+ int regsToRefresh = Integer.parseInt(args[1]); // 0 means no regions to refresh at the end of cycle
+ int timeout = Integer.parseInt(args[2]); // in seconds, test should stop working eariler
+ new TestStressRSetCoarsening(objectsPerRegion, regsToRefresh, timeout).go();
+ }
+
+ private static final long KB = 1024;
+ private static final long MB = 1024 * KB;
+
+ private static final WhiteBox WB = WhiteBox.getWhiteBox();
+
+ public final Object[][] storage;
+
+ /**
+ * Number of objects per region. This is a test parameter.
+ */
+ public final int K;
+
+ /**
+ * Length of object array: sizeOf(Object[N]) ~= regionSize / K
+ * N will be calculated as function of K.
+ */
+ public final int N;
+
+ /**
+ * How many regions involved into testing.
+ * Will be calculated as heapFractionToAllocate * freeRegionCount.
+ */
+ public final int regionCount;
+
+ /**
+ * How much heap to use.
+ */
+ public final float heapFractionToAllocate = 0.9f;
+
+ /**
+ * How many regions to be refreshed at the end of cycle.
+ * This is a test parameter.
+ */
+ public final int regsToRefresh;
+
+ /**
+ * Initial time.
+ */
+ public final long start;
+
+ /**
+ * Time when the test should stop working.
+ */
+ public final long finishAt;
+
+ /**
+ * Does pre-calculation and allocate necessary objects.
+ *
+ * @param objPerRegions how many objects per G1 heap region
+ */
+ TestStressRSetCoarsening(int objPerRegions, int regsToRefresh, int timeout) {
+ this.K = objPerRegions;
+ this.regsToRefresh = regsToRefresh;
+ this.start = System.currentTimeMillis();
+ this.finishAt = start + timeout * 900; // 10% ahead of jtreg timeout
+
+ long regionSize = WB.g1RegionSize();
+
+ // How many free regions
+ Runtime rt = Runtime.getRuntime();
+ long used = rt.totalMemory() - rt.freeMemory();
+ long totalFree = rt.maxMemory() - used;
+ regionCount = (int) ((totalFree / regionSize) * heapFractionToAllocate);
+ long toAllocate = regionCount * regionSize;
+ System.out.println("%% Test parameters");
+ System.out.println("%% Objects per region : " + K);
+ System.out.println("%% Heap fraction to allocate : " + (int) (heapFractionToAllocate * 100) + "%");
+ System.out.println("%% Regions to refresh to provoke GC: " + regsToRefresh);
+
+ System.out.println("%% Memory");
+ System.out.println("%% used : " + used / MB + "M");
+ System.out.println("%% available : " + totalFree / MB + "M");
+ System.out.println("%% to allocate : " + toAllocate / MB + "M");
+ System.out.println("%% (in regs) : " + regionCount);
+ System.out.println("%% G1 Region Size: " + regionSize / MB + "M");
+
+ int refSize = WB.getHeapOopSize();
+
+ // Calculate N: K*sizeOf(Object[N]) ~= regionSize
+ // sizeOf(Object[N]) ~= (N+4)*refSize
+ // ==>
+ // N = regionSize / K / refSize - 4;
+ N = (int) ((regionSize / K) / refSize) - 5;
+
+ /*
+ * --------------
+ * region0 storage[0] = new Object[N]
+ * ...
+ * storage[K-1] = new Object[N]
+ * ---------------
+ * region1 storage[K] = new Object[N]
+ * ...
+ * storage[2*K - 1] = new Object[N]
+ * --------------
+ * ...
+ * --------------
+ * regionX storage[X*K] = new Object[N]
+ * ...
+ * storage[(X+1)*K -1] = new Object[N]
+ * where X = HeapFraction * TotalRegions
+ * -------------
+ */
+ System.out.println("%% Objects");
+ System.out.println("%% N (array length) : " + N);
+ System.out.println("%% K (objects in regions): " + K);
+ System.out.println("%% Reference size : " + refSize);
+ System.out.println("%% Approximate obj size : " + (N + 2) * refSize / KB + "K)");
+
+ storage = new Object[regionCount * K][];
+ for (int i = 0; i < storage.length; i++) {
+ storage[i] = new Object[N];
+ }
+ }
+
+ public void go() throws InterruptedException {
+ // threshold for sparce -> fine
+ final int FINE = WB.getIntxVMFlag("G1RSetSparseRegionEntries").intValue();
+
+ // threshold for fine -> coarse
+ final int COARSE = WB.getIntxVMFlag("G1RSetRegionEntries").intValue();
+
+ // regToRegRefCounts - array of reference counts from region to region
+ // at the the end of iteration.
+ // The number of test iterations is array length - 1.
+ // If c[i] > c[i-1] then during the iteration i more references will
+ // be created.
+ // If c[i] < c[i-1] then some referenes will be cleaned.
+ int[] regToRegRefCounts = {0, FINE / 2, 0, FINE, (FINE + COARSE) / 2, 0,
+ COARSE, COARSE + 10, FINE + 1, FINE / 2, 0};
+
+ // For progress tracking
+ int[] progress = new int[regToRegRefCounts.length];
+ progress[0] = 0;
+ for (int i = 1; i < regToRegRefCounts.length; i++) {
+ progress[i] = progress[i - 1] + Math.abs(regToRegRefCounts[i] - regToRegRefCounts[i - 1]);
+ }
+ try {
+ for (int i = 1; i < regToRegRefCounts.length; i++) {
+ int pre = regToRegRefCounts[i - 1];
+ int cur = regToRegRefCounts[i];
+ float prog = ((float) progress[i - 1] / progress[progress.length - 1]);
+
+ System.out.println("%% step " + i
+ + " out of " + (regToRegRefCounts.length - 1)
+ + " (~" + (int) (100 * prog) + "% done)");
+ System.out.println("%% " + pre + " --> " + cur);
+ for (int to = 0; to < regionCount; to++) {
+ // Select a celebrity object that we will install references to.
+ // The celebrity will be referred from all other regions.
+ // If the number of references after should be less than they
+ // were before, select NULL.
+ Object celebrity = cur > pre ? storage[to * K] : null;
+ for (int from = 0; from < regionCount; from++) {
+ if (to == from) {
+ continue; // no need to refer to itself
+ }
+
+ int step = cur > pre ? +1 : -1;
+ for (int rn = pre; rn != cur; rn += step) {
+ storage[getY(to, from, rn)][getX(to, from, rn)] = celebrity;
+ if (System.currentTimeMillis() > finishAt) {
+ throw new TimeoutException();
+ }
+ }
+ }
+ }
+ if (pre > cur) {
+ // Number of references went down.
+ // Need to provoke recalculation of RSet.
+ WB.g1StartConcMarkCycle();
+ while (WB.g1InConcurrentMark()) {
+ Thread.sleep(1);
+ }
+ }
+
+ // To force the use of rememebered set entries we need to provoke a GC.
+ // To induce some fragmentation, and some mixed GCs, we need
+ // to make a few objects unreachable.
+ for (int toClean = i * regsToRefresh; toClean < (i + 1) * regsToRefresh; toClean++) {
+ int to = toClean % regionCount;
+ // Need to remove all references from all regions to the region 'to'
+ for (int from = 0; from < regionCount; from++) {
+ if (to == from) {
+ continue; // no need to refer to itself
+ }
+ for (int rn = 0; rn <= cur; rn++) {
+ storage[getY(to, from, rn)][getX(to, from, rn)] = null;
+ }
+ }
+ // 'Refresh' storage elements for the region 'to'
+ // After that loop all 'old' objects in the region 'to'
+ // should become unreachable.
+ for (int k = 0; k < K; k++) {
+ storage[(to * K + k) % storage.length] = new Object[N];
+ }
+ }
+ }
+ } catch (TimeoutException e) {
+ System.out.println("%% TIMEOUT!!!");
+ }
+ long now = System.currentTimeMillis();
+ System.out.println("%% Summary");
+ System.out.println("%% Time spent : " + ((now - start) / 1000) + " seconds");
+ System.out.println("%% Free memory left : " + Runtime.getRuntime().freeMemory() / KB + "K");
+ System.out.println("%% Test passed");
+ }
+
+ /**
+ * Returns X index in the Storage of the reference #rn from the region
+ * 'from' to the region 'to'.
+ *
+ * @param to region # to refer to
+ * @param from region # to refer from
+ * @param rn number of reference
+ *
+ * @return X index in the range: [0 ... N-1]
+ */
+ private int getX(int to, int from, int rn) {
+ return (rn * regionCount + to) % N;
+ }
+
+ /**
+ * Returns Y index in the Storage of the reference #rn from the region
+ * 'from' to the region 'to'.
+ *
+ * @param to region # to refer to
+ * @param from region # to refer from
+ * @param rn number of reference
+ *
+ * @return Y index in the range: [0 ... K*regionCount -1]
+ */
+ private int getY(int to, int from, int rn) {
+ return ((rn * regionCount + to) / N + from * K) % (regionCount * K);
+ }
+}
+