/* * 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) ; } }