1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import com.sun.management.HotSpotDiagnosticMXBean; 25 import java.lang.management.ManagementFactory; 26 import java.util.Date; 27 28 /* 29 * @test TestUpAndDownRSet.java 30 * @key stress 31 * @requires vm.gc=="G1" | vm.gc=="null" 32 * @requires os.maxMemory > 3G 33 * 34 * @summary Stress G1 Remembered Set by creating a lot of cross region links 35 * @modules jdk.management 36 * @run main/othervm -XX:+UseG1GC -Xmx2G -XX:G1HeapRegionSize=1m TestUpAndDownRSet 10000 37 * @run main/othervm -XX:+UseG1GC -Xmx2G -XX:G1HeapRegionSize=1m TestUpAndDownRSet 10 38 * @run main/othervm -XX:+UseG1GC -Xmx2G -XX:G1HeapRegionSize=1m TestUpAndDownRSet 1 39 * @run main/othervm -XX:+UseG1GC -Xmx2G -XX:G1HeapRegionSize=8m TestUpAndDownRSet 2 40 * @run main/othervm -XX:+UseG1GC -Xmx2G -XX:G1HeapRegionSize=32m TestUpAndDownRSet 42 41 */ 42 43 /** 44 * What the test does: 45 * Step1: 46 * Fill out ~90% of the heap with objects of G1HeapRegionSize*0.4 size. 47 * Two objects per region will be allocated. 48 * Step2: 49 * Link each second object to each second object. 50 * Expecting RSet will grow to a high value. 51 * Step3: 52 * Clear references created on the Step2 53 * Expecting RSet will go down. 54 * Continue Step2 and Step3 for 1000 times or until 10 seconds. 55 * 56 * The test expects: no crash and no timeouts. 57 */ 58 public class TestUpAndDownRSet { 59 60 public static void main(String... args) { 61 if (args.length != 1) { 62 throw new IllegalArgumentException("Wrong number of arguments " + args.length); 63 } 64 new TestUpAndDownRSet(Integer.parseInt(args[0])).go(); 65 } 66 67 private static final long KB = 1024; 68 private static final long MB = 1024 * KB; 69 70 private static final HotSpotDiagnosticMXBean HS_MX_BEAN 71 = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class); 72 73 public final Object[][] storage; 74 75 // how many objects to refer from objects 76 public final int refNumber; 77 78 // how many objects should be in each G1 heap region 79 public final int objPerRegion; 80 81 /** 82 * Does pre-calculation and allocate necessary objects. 83 * 84 * @param objPerRegions how many objects per G1 heap region 85 */ 86 TestUpAndDownRSet(int objPerRegion) { 87 this.objPerRegion = objPerRegion; 88 89 long regionSize = getG1RegionSize(); 90 91 // how many free regions 92 Runtime rt = Runtime.getRuntime(); 93 long used = rt.totalMemory() - rt.freeMemory(); 94 long totalFree = rt.maxMemory() - used; 95 long toAllocate = (long) (0.9 * totalFree); // we want to leave some free memory 96 int regionCount = (int) (toAllocate / regionSize); 97 98 System.out.println("%% Memory"); 99 System.out.println("%% used : " + used / MB + "M"); 100 System.out.println("%% available : " + totalFree / MB + "M"); 101 System.out.println("%% to allocate : " + toAllocate / MB + "M"); 102 System.out.println("%% G1 Region Size: " + regionSize / MB + "M"); 103 104 105 byte refSize = getRefSize(); 106 107 // We will fill out the heap with objects of equal size, 108 // allocating k objects per region. 109 // Each object will be an array of objects (Object[N]): 110 // storage[i] = new Object[N] 111 // We want to calculate N to meet the requirements: 112 // k * sizeOf(Object[N]) < regionSize 113 // (k+1) * sizeOf(Object[N]) > regionSize 114 // sizeOf(Object[N]) should be approximately regionSize/k 115 116 int sizeOfObjectN = (int) regionSize / objPerRegion; 117 int arrayHeaderSize = 12 + refSize; // 16 or 20 if -XX:-UseCompressedOops 118 // we need an approximate value here 119 int N = (int) ((sizeOfObjectN - arrayHeaderSize) / refSize); 120 121 // we will expect: storage[i] and storage[i+k] will be allocated in 122 // different G1 regions. 123 storage = new Object[regionCount * objPerRegion][]; 124 for (int i = 0; i < storage.length; i++) { 125 storage[i] = new Object[N]; 126 } 127 128 // in most cases refNumber == regionCount 129 // just to protect cases when: regionCount > N (a lot of very small regions) 130 refNumber = java.lang.Math.min(regionCount, N); 131 132 System.out.println("%% Objects: "); 133 System.out.println("%% Number of objects: " + storage.length); 134 System.out.println("%% objects in region: " + objPerRegion); 135 System.out.println("%% N (array length) : " + N); 136 System.out.println("%% Reference size : " + refSize); 137 System.out.println("%% Approximate size : " + N * refSize + "(" + N * refSize / KB + "K)"); 138 System.out.println("%% Reference number : " + refNumber); 139 } 140 141 142 // we don't want to execute longer than 10 seconds 143 private final long MAX_EXECUTION_TIME = 10_000; 144 145 // we don't want to perform over 1000 iterations 146 private final int MAX_ITER = 1000; 147 148 public void go() { 149 150 int iter = 0; 151 long start = new Date().getTime(); 152 long now = 0; 153 while (iter < MAX_ITER && now < start + MAX_EXECUTION_TIME) { 154 // Remembered Set UP 155 for (int i = 0; i < storage.length; i += objPerRegion) { 156 for (int j = 0; j < refNumber; j++) { 157 storage[i][j] = storage[j * objPerRegion]; 158 } 159 } 160 // Remembered Set DOWN 161 for (int i = 0; i < storage.length; i += objPerRegion) { 162 for (int j = 0; j < refNumber; j++) { 163 storage[i][j] = null; 164 } 165 } 166 iter++; 167 now = new Date().getTime(); 168 } 169 System.out.println("%% Execution:"); 170 System.out.println("%% Number of iterations: " + iter); 171 System.out.println("%% Time spent: " + ((now - start)/1000) + "seconds"); 172 System.out.println("%% Free memory left: " + Runtime.getRuntime().freeMemory()/MB + "M"); 173 System.out.println("%% Test passed"); 174 } 175 176 177 /** 178 * @return 4 unless UseCompressedOops flag is explicitely disabled. 179 */ 180 private static byte getRefSize() { 181 try { 182 if ("false".equals(HS_MX_BEAN.getVMOption("UseCompressedOops").getValue())) { 183 return 8; 184 } 185 } catch (Exception e) { 186 // UseCompressedOops is not supported flag ... 187 } 188 return 4; 189 } 190 191 /** 192 * @return G1 Heap Region Size 193 */ 194 private static long getG1RegionSize() { 195 try { 196 return Long.parseLong(HS_MX_BEAN.getVMOption("G1HeapRegionSize").getValue()); 197 } catch (Exception e) { 198 throw new RuntimeException("Unable to detect G1 HeapRegionSize " + e.getMessage(), e); 199 } 200 } 201 } 202