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