--- /dev/null Wed Dec 30 20:18:20 2015 +++ new/test/stress/gc/TestUpAndDownRSet.java Wed Dec 30 20:18:50 2015 @@ -0,0 +1,290 @@ +/* + * 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. + */ + +import sun.hotspot.WhiteBox; + +/* + * @test TestUpAndDownRSet.java + * @key stress + * @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 TestUpAndDownRSet 1 0 + * @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 TestUpAndDownRSet 1 10 + * @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 TestUpAndDownRSet 42 10 + * @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 TestUpAndDownRSet 2 0 + * @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 TestUpAndDownRSet 500 0 + * @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 TestUpAndDownRSet 10 10 + */ + +/** + * 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] < 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. + */ +public class TestUpAndDownRSet { + + public static void main(String... args) throws InterruptedException { + if (args.length != 2) { + 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 + new TestUpAndDownRSet(objectsPerRegion, regsToRefresh).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; + + /** + * Does pre-calculation and allocate necessary objects. + * + * @param objPerRegions how many objects per G1 heap region + */ + TestUpAndDownRSet(int objPerRegions, int regsToRefresh) { + this.K = objPerRegions; + this.regsToRefresh = regsToRefresh; + + 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("%% Object per region: " + K); + System.out.println("%% Heap 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 { + + int iter = 0; + long start = System.currentTimeMillis(); + + // Thresholds for sparse, fine, coarse (need to know real values) + final int FINE = 100; // threshold for sparce -> fine + final int COARSE = 300; // threshold for fine -> coarse + + int[] regToRegRefCounts = {0, FINE/5, 0, FINE, (FINE+COARSE)/2, 0, COARSE, FINE, FINE/3, 0}; + for (int i = 1; i < regToRegRefCounts.length; i++) { + int pre = regToRegRefCounts[i-1]; + int cur = regToRegRefCounts[i]; + System.out.println("%% " + pre + " --> " + cur); + + for (int to = 0; to < regionCount; to++) { + // an object from "to" region + // if pre > cur, then we will forget + Object star = 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)] = star; + } + } + } + if (pre > cur) { + // number of references went down + // need to provoke recalculation of RSet + WB.g1StartConcMarkCycle(); + while (WB.g1InConcurrentMark()) { + Thread.sleep(1); + } + } + + // to cause RSet reading we need to provoke GC + // for that purpose we will renew some thing in storage + 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' + for (int k = 0; k < K; k++) { + storage[(to*K + k) % storage.length ] = new Object[N]; + } + } + + + } + long now = System.currentTimeMillis(); + System.out.println("%% Execution:"); + System.out.println("%% Number of iterations: " + iter); + System.out.println("%% Time spent: " + ((now - start)/1000) + " seconds"); + System.out.println("%% Free memory left: " + Runtime.getRuntime().freeMemory()/MB + "M"); + 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) ; + } +} +