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