1 /* 2 * Copyright (c) 2016, 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 java.util.concurrent.TimeoutException; 25 import sun.hotspot.WhiteBox; 26 27 /* 28 * @test TestStressRSetCoarsening.java 29 * @key stress 30 * @bug 8146984 8147087 31 * @requires vm.gc.G1 32 * @requires os.maxMemory > 3G 33 * @requires vm.opt.MaxGCPauseMillis == "null" 34 * 35 * @summary Stress G1 Remembered Set by creating a lot of cross region links 36 * @modules java.base/jdk.internal.misc 37 * @library /test/lib 38 * @build sun.hotspot.WhiteBox 39 * @run main ClassFileInstaller sun.hotspot.WhiteBox 40 * sun.hotspot.WhiteBox$WhiteBoxPermission 41 * @run main/othervm/timeout=300 42 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 43 * -XX:+UseG1GC -Xlog:gc* -XX:MaxGCPauseMillis=1000 44 * -Xmx500m -XX:G1HeapRegionSize=1m TestStressRSetCoarsening 1 0 300 45 * @run main/othervm/timeout=300 46 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 47 * -XX:+UseG1GC -Xlog:gc* -XX:MaxGCPauseMillis=1000 48 * -Xmx500m -XX:G1HeapRegionSize=8m TestStressRSetCoarsening 1 10 300 49 * @run main/othervm/timeout=300 50 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 51 * -XX:+UseG1GC -Xlog:gc* -XX:MaxGCPauseMillis=1000 52 * -Xmx500m -XX:G1HeapRegionSize=32m TestStressRSetCoarsening 42 10 300 53 * @run main/othervm/timeout=300 54 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 55 * -XX:+UseG1GC -Xlog:gc* -XX:MaxGCPauseMillis=1000 56 * -Xmx500m -XX:G1HeapRegionSize=1m TestStressRSetCoarsening 2 0 300 57 * @run main/othervm/timeout=1800 58 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 59 * -XX:+UseG1GC -Xlog:gc* -XX:MaxGCPauseMillis=1000 60 * -Xmx1G -XX:G1HeapRegionSize=1m TestStressRSetCoarsening 500 0 1800 61 * @run main/othervm/timeout=1800 62 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 63 * -XX:+UseG1GC -Xlog:gc* -XX:MaxGCPauseMillis=1000 64 * -Xmx1G -XX:G1HeapRegionSize=1m TestStressRSetCoarsening 10 10 1800 65 */ 66 67 /** 68 * What the test does. 69 * Preparation stage: 70 * Fill out ~90% of the heap with objects, each object is an object array. 71 * If we want to allocate K objects per region, we calculate N to meet: 72 * sizeOf(Object[N]) ~= regionSize / K 73 * Stress stage: 74 * No more allocation, so no more GC. 75 * We will perform a number of iterations. On each iteration i, 76 * for each pair of regions Rx and Ry we will set c[i] references 77 * from Rx to Ry. If c[i] less than c[i-1] at the end of iteration 78 * concurrent mark cycle will be initiated (to recalculate remembered sets). 79 * As the result RSet will be growing up and down, up and down many times. 80 * 81 * The test expects: no crash and no timeouts. 82 * 83 * Test Parameters: 84 * args[0] - number of objects per Heap Region (1 - means humongous) 85 * args[1] - number of regions to refresh to provoke GC at the end of cycle. 86 * (0 - means no GC, i.e. no reading from RSet) 87 * args[2] - timeout in seconds (to stop execution to avoid jtreg timeout) 88 */ 89 public class TestStressRSetCoarsening { 90 91 public static void main(String... args) throws InterruptedException { 92 if (args.length != 3) { 93 throw new IllegalArgumentException("Wrong number of arguments " + args.length); 94 } 95 int objectsPerRegion = Integer.parseInt(args[0]); // 1 means humongous 96 int regsToRefresh = Integer.parseInt(args[1]); // 0 means no regions to refresh at the end of cycle 97 int timeout = Integer.parseInt(args[2]); // in seconds, test should stop working eariler 98 new TestStressRSetCoarsening(objectsPerRegion, regsToRefresh, timeout).go(); 99 } 100 101 private static final long KB = 1024; 102 private static final long MB = 1024 * KB; 103 104 private static final WhiteBox WB = WhiteBox.getWhiteBox(); 105 106 public final ObjStorage storage; 107 108 /** 109 * Number of objects per region. This is a test parameter. 110 */ 111 public final int K; 112 113 /** 114 * Length of object array: sizeOf(Object[N]) ~= regionSize / K 115 * N will be calculated as function of K. 116 */ 117 public final int N; 118 119 /** 120 * How many regions involved into testing. 121 * Will be calculated as heapFractionToAllocate * freeRegionCount. 122 */ 123 public final int regionCount; 124 125 /** 126 * How much heap to use. 127 */ 128 public final float heapFractionToAllocate = 0.9f; 129 130 /** 131 * How many regions to be refreshed at the end of cycle. 132 * This is a test parameter. 133 */ 134 public final int regsToRefresh; 135 136 /** 137 * Initial time. 138 */ 139 public final long start; 140 141 /** 142 * Time when the test should stop working. 143 */ 144 public final long finishAt; 145 146 /** 147 * Does pre-calculation and allocate necessary objects. 148 * 149 * @param objPerRegions how many objects per G1 heap region 150 */ 151 TestStressRSetCoarsening(int objPerRegions, int regsToRefresh, int timeout) { 152 this.K = objPerRegions; 153 this.regsToRefresh = regsToRefresh; 154 this.start = System.currentTimeMillis(); 155 this.finishAt = start + timeout * 900; // 10% ahead of jtreg timeout 156 157 long regionSize = WB.g1RegionSize(); 158 159 // How many free regions 160 Runtime rt = Runtime.getRuntime(); 161 long used = rt.totalMemory() - rt.freeMemory(); 162 long totalFree = rt.maxMemory() - used; 163 regionCount = (int) ((totalFree / regionSize) * heapFractionToAllocate); 164 long toAllocate = regionCount * regionSize; 165 long freeMemoryLimit = totalFree - toAllocate; 166 167 System.out.println("%% Test parameters"); 168 System.out.println("%% Objects per region : " + K); 169 System.out.println("%% Heap fraction to allocate : " + (int) (heapFractionToAllocate * 100) + "%"); 170 System.out.println("%% Regions to refresh to provoke GC: " + regsToRefresh); 171 172 System.out.println("%% Memory"); 173 System.out.println("%% used : " + used / MB + "M"); 174 System.out.println("%% available : " + totalFree / MB + "M"); 175 System.out.println("%% to allocate : " + toAllocate / MB + "M"); 176 System.out.println("%% (in regs) : " + regionCount); 177 System.out.println("%% G1 Region Size: " + regionSize / MB + "M"); 178 179 int refSize = WB.getHeapOopSize(); 180 181 // Calculate N: K*sizeOf(Object[N]) ~= regionSize 182 // sizeOf(Object[N]) ~= (N+4)*refSize 183 // ==> 184 // N = regionSize / K / refSize - 4; 185 int n = (int) ((regionSize / K) / refSize) - 5; // best guess 186 long objSize = WB.getObjectSize(new Object[n]); 187 while (K*objSize > regionSize) { // adjust to avoid OOME 188 n = n - 1; 189 objSize = WB.getObjectSize(new Object[n]); 190 } 191 N = n; 192 193 /* 194 * -------------- 195 * region0 storage[0] = new Object[N] 196 * ... 197 * storage[K-1] = new Object[N] 198 * --------------- 199 * region1 storage[K] = new Object[N] 200 * ... 201 * storage[2*K - 1] = new Object[N] 202 * -------------- 203 * ... 204 * -------------- 205 * regionX storage[X*K] = new Object[N] 206 * ... 207 * storage[(X+1)*K -1] = new Object[N] 208 * where X = HeapFraction * TotalRegions 209 * ------------- 210 */ 211 System.out.println("%% Objects"); 212 System.out.println("%% N (array length) : " + N); 213 System.out.println("%% K (objects in regions): " + K); 214 System.out.println("%% Object size : " + objSize + 215 " (sizeOf(new Object[" + N + "])"); 216 System.out.println("%% Reference size : " + refSize); 217 218 // Maximum number of objects to allocate is regionCount * K. 219 storage = new ObjStorage(regionCount * K); 220 221 // Add objects as long as there is space in the storage 222 // and we haven't used more memory than planned. 223 while (!storage.isFull() && (rt.maxMemory() - used) > freeMemoryLimit) { 224 storage.addArray(new Object[N]); 225 // Update used memory 226 used = rt.totalMemory() - rt.freeMemory(); 227 } 228 } 229 230 public void go() throws InterruptedException { 231 // threshold for sparce -> fine 232 final int FINE = WB.getIntxVMFlag("G1RSetSparseRegionEntries").intValue(); 233 234 // threshold for fine -> coarse 235 final int COARSE = WB.getIntxVMFlag("G1RSetRegionEntries").intValue(); 236 237 // regToRegRefCounts - array of reference counts from region to region 238 // at the the end of iteration. 239 // The number of test iterations is array length - 1. 240 // If c[i] > c[i-1] then during the iteration i more references will 241 // be created. 242 // If c[i] < c[i-1] then some referenes will be cleaned. 243 int[] regToRegRefCounts = {0, FINE / 2, 0, FINE, (FINE + COARSE) / 2, 0, 244 COARSE, COARSE + 10, FINE + 1, FINE / 2, 0}; 245 246 // For progress tracking 247 int[] progress = new int[regToRegRefCounts.length]; 248 progress[0] = 0; 249 for (int i = 1; i < regToRegRefCounts.length; i++) { 250 progress[i] = progress[i - 1] + Math.abs(regToRegRefCounts[i] - regToRegRefCounts[i - 1]); 251 } 252 try { 253 for (int i = 1; i < regToRegRefCounts.length; i++) { 254 int pre = regToRegRefCounts[i - 1]; 255 int cur = regToRegRefCounts[i]; 256 float prog = ((float) progress[i - 1] / progress[progress.length - 1]); 257 258 System.out.println("%% step " + i 259 + " out of " + (regToRegRefCounts.length - 1) 260 + " (~" + (int) (100 * prog) + "% done)"); 261 System.out.println("%% " + pre + " --> " + cur); 262 for (int to = 0; to < regionCount; to++) { 263 // Select a celebrity object that we will install references to. 264 // The celebrity will be referred from all other regions. 265 // If the number of references after should be less than they 266 // were before, select NULL. 267 Object celebrity = cur > pre ? storage.getArrayAt(to * K) : null; 268 for (int from = 0; from < regionCount; from++) { 269 if (to == from) { 270 continue; // no need to refer to itself 271 } 272 273 int step = cur > pre ? +1 : -1; 274 for (int rn = pre; rn != cur; rn += step) { 275 Object[] rnArray = storage.getArrayAt(getY(to, from, rn)); 276 rnArray[getX(to, from, rn)] = celebrity; 277 if (System.currentTimeMillis() > finishAt) { 278 throw new TimeoutException(); 279 } 280 } 281 } 282 } 283 if (pre > cur) { 284 // Number of references went down. 285 // Need to provoke recalculation of RSet. 286 WB.g1StartConcMarkCycle(); 287 while (WB.g1InConcurrentMark()) { 288 Thread.sleep(1); 289 } 290 } 291 292 // To force the use of rememebered set entries we need to provoke a GC. 293 // To induce some fragmentation, and some mixed GCs, we need 294 // to make a few objects unreachable. 295 for (int toClean = i * regsToRefresh; toClean < (i + 1) * regsToRefresh; toClean++) { 296 int to = toClean % regionCount; 297 // Need to remove all references from all regions to the region 'to' 298 for (int from = 0; from < regionCount; from++) { 299 if (to == from) { 300 continue; // no need to refer to itself 301 } 302 for (int rn = 0; rn <= cur; rn++) { 303 Object[] rnArray = storage.getArrayAt(getY(to, from, rn)); 304 rnArray[getX(to, from, rn)] = null; 305 } 306 } 307 // 'Refresh' storage elements for the region 'to' 308 // After that loop all 'old' objects in the region 'to' 309 // should become unreachable. 310 for (int k = 0; k < K; k++) { 311 storage.setArrayAt(to * K + k, new Object[N]); 312 } 313 } 314 } 315 } catch (TimeoutException e) { 316 System.out.println("%% TIMEOUT!!!"); 317 } 318 long now = System.currentTimeMillis(); 319 System.out.println("%% Summary"); 320 System.out.println("%% Time spent : " + ((now - start) / 1000) + " seconds"); 321 System.out.println("%% Free memory left : " + Runtime.getRuntime().freeMemory() / KB + "K"); 322 System.out.println("%% Test passed"); 323 } 324 325 /** 326 * Returns X index in the Storage of the reference #rn from the region 327 * 'from' to the region 'to'. 328 * 329 * @param to region # to refer to 330 * @param from region # to refer from 331 * @param rn number of reference 332 * 333 * @return X index in the range: [0 ... N-1] 334 */ 335 private int getX(int to, int from, int rn) { 336 return (rn * regionCount + to) % N; 337 } 338 339 /** 340 * Returns Y index in the Storage of the reference #rn from the region 341 * 'from' to the region 'to'. 342 * 343 * @param to region # to refer to 344 * @param from region # to refer from 345 * @param rn number of reference 346 * 347 * @return Y index in the range: [0 ... K*regionCount -1] 348 */ 349 private int getY(int to, int from, int rn) { 350 return ((rn * regionCount + to) / N + from * K) % (regionCount * K); 351 } 352 } 353 354 //Helper class to encapsulate the object array storage. 355 class ObjStorage { 356 public final Object[][] storage; 357 public int usedCount; 358 359 ObjStorage(int size) { 360 storage = new Object[size][]; 361 usedCount = 0; 362 } 363 364 public boolean isFull() { 365 return usedCount >= storage.length; 366 } 367 368 public void addArray(Object[] objects) { 369 if (isFull()) { 370 throw new IllegalStateException("Storage full maximum number of allowed elements: " + usedCount); 371 } 372 storage[usedCount++] = objects; 373 } 374 375 // Limit by usedCount since memory limits can cause the storage 376 // to have unused slots in the end. 377 public void setArrayAt(int i, Object[] objects) { 378 storage[i % usedCount] = objects; 379 } 380 381 // Limit by usedCount since memory limits can cause the storage 382 // to have unused slots in the end. 383 public Object[] getArrayAt(int i) { 384 return storage[i % usedCount]; 385 } 386 }