--- /dev/null Fri Nov 13 21:04:48 2015 +++ new/test/stress/gc/TestUpAndDownRSet.java Fri Nov 13 21:07:51 2015 @@ -0,0 +1,202 @@ +/* + * 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 com.sun.management.HotSpotDiagnosticMXBean; +import java.lang.management.ManagementFactory; +import java.util.Date; + +/* + * @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 jdk.management + * @run main/othervm -XX:+UseG1GC -Xmx2G -XX:G1HeapRegionSize=1m TestUpAndDownRSet 10000 + * @run main/othervm -XX:+UseG1GC -Xmx2G -XX:G1HeapRegionSize=1m TestUpAndDownRSet 10 + * @run main/othervm -XX:+UseG1GC -Xmx2G -XX:G1HeapRegionSize=1m TestUpAndDownRSet 1 + * @run main/othervm -XX:+UseG1GC -Xmx2G -XX:G1HeapRegionSize=8m TestUpAndDownRSet 2 + * @run main/othervm -XX:+UseG1GC -Xmx2G -XX:G1HeapRegionSize=32m TestUpAndDownRSet 42 + */ + +/** + * What the test does: + * Step1: + * Fill out ~90% of the heap with objects of G1HeapRegionSize*0.4 size. + * Two objects per region will be allocated. + * Step2: + * Link each second object to each second object. + * Expecting RSet will grow to a high value. + * Step3: + * Clear references created on the Step2 + * Expecting RSet will go down. + * Continue Step2 and Step3 for 1000 times or until 10 seconds. + * + * The test expects: no crash and no timeouts. + */ +public class TestUpAndDownRSet { + + public static void main(String... args) { + if (args.length != 1) { + throw new IllegalArgumentException("Wrong number of arguments " + args.length); + } + new TestUpAndDownRSet(Integer.parseInt(args[0])).go(); + } + + private static final long KB = 1024; + private static final long MB = 1024 * KB; + + private static final HotSpotDiagnosticMXBean HS_MX_BEAN + = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class); + + public final Object[][] storage; + + // how many objects to refer from objects + public final int refNumber; + + // how many objects should be in each G1 heap region + public final int objPerRegion; + + /** + * Does pre-calculation and allocate necessary objects. + * + * @param objPerRegions how many objects per G1 heap region + */ + TestUpAndDownRSet(int objPerRegion) { + this.objPerRegion = objPerRegion; + + long regionSize = getG1RegionSize(); + + // how many free regions + Runtime rt = Runtime.getRuntime(); + long used = rt.totalMemory() - rt.freeMemory(); + long totalFree = rt.maxMemory() - used; + long toAllocate = (long) (0.9 * totalFree); // we want to leave some free memory + int regionCount = (int) (toAllocate / regionSize); + + 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("%% G1 Region Size: " + regionSize / MB + "M"); + + + byte refSize = getRefSize(); + + // We will fill out the heap with objects of equal size, + // allocating k objects per region. + // Each object will be an array of objects (Object[N]): + // storage[i] = new Object[N] + // We want to calculate N to meet the requirements: + // k * sizeOf(Object[N]) < regionSize + // (k+1) * sizeOf(Object[N]) > regionSize + // sizeOf(Object[N]) should be approximately regionSize/k + + int sizeOfObjectN = (int) regionSize / objPerRegion; + int arrayHeaderSize = 12 + refSize; // 16 or 20 if -XX:-UseCompressedOops + // we need an approximate value here + int N = (int) ((sizeOfObjectN - arrayHeaderSize) / refSize); + + // we will expect: storage[i] and storage[i+k] will be allocated in + // different G1 regions. + storage = new Object[regionCount * objPerRegion][]; + for (int i = 0; i < storage.length; i++) { + storage[i] = new Object[N]; + } + + // in most cases refNumber == regionCount + // just to protect cases when: regionCount > N (a lot of very small regions) + refNumber = java.lang.Math.min(regionCount, N); + + System.out.println("%% Objects: "); + System.out.println("%% Number of objects: " + storage.length); + System.out.println("%% objects in region: " + objPerRegion); + System.out.println("%% N (array length) : " + N); + System.out.println("%% Reference size : " + refSize); + System.out.println("%% Approximate size : " + N * refSize + "(" + N * refSize / KB + "K)"); + System.out.println("%% Reference number : " + refNumber); + } + + + // we don't want to execute longer than 10 seconds + private final long MAX_EXECUTION_TIME = 10_000; + + // we don't want to perform over 1000 iterations + private final int MAX_ITER = 1000; + + public void go() { + + int iter = 0; + long start = new Date().getTime(); + long now = 0; + while (iter < MAX_ITER && now < start + MAX_EXECUTION_TIME) { + // Remembered Set UP + for (int i = 0; i < storage.length; i += objPerRegion) { + for (int j = 0; j < refNumber; j++) { + storage[i][j] = storage[j * objPerRegion]; + } + } + // Remembered Set DOWN + for (int i = 0; i < storage.length; i += objPerRegion) { + for (int j = 0; j < refNumber; j++) { + storage[i][j] = null; + } + } + iter++; + now = new Date().getTime(); + } + 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"); + } + + + /** + * @return 4 unless UseCompressedOops flag is explicitely disabled. + */ + private static byte getRefSize() { + try { + if ("false".equals(HS_MX_BEAN.getVMOption("UseCompressedOops").getValue())) { + return 8; + } + } catch (Exception e) { + // UseCompressedOops is not supported flag ... + } + return 4; + } + + /** + * @return G1 Heap Region Size + */ + private static long getG1RegionSize() { + try { + return Long.parseLong(HS_MX_BEAN.getVMOption("G1HeapRegionSize").getValue()); + } catch (Exception e) { + throw new RuntimeException("Unable to detect G1 HeapRegionSize " + e.getMessage(), e); + } + } +} +