1 /*
2 * Copyright (c) 2014, 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 static com.oracle.java.testlibrary.Asserts.assertLessThanOrEqual;
25 import com.oracle.java.testlibrary.OutputAnalyzer;
26 import com.oracle.java.testlibrary.Platform;
27 import com.oracle.java.testlibrary.ProcessTools;
28 import com.oracle.java.testlibrary.Utils;
29 import java.io.IOException;
30 import java.lang.management.ManagementFactory;
31 import java.lang.management.MemoryUsage;
32 import java.text.DecimalFormat;
33 import java.text.DecimalFormatSymbols;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.LinkedList;
38 import java.util.List;
39 import sun.misc.Unsafe;
40
41 public class TestShrinkAuxiliaryData {
42
43 private final static String[] initialOpts = new String[]{
44 "-XX:MinHeapFreeRatio=10",
45 "-XX:MaxHeapFreeRatio=11",
46 "-XX:+UseG1GC",
47 "-XX:G1HeapRegionSize=1m",
48 "-XX:-ExplicitGCInvokesConcurrent",
49 "-XX:+PrintGCDetails"
50 };
51
52 private final int RSetCacheSize;
53
54 protected TestShrinkAuxiliaryData(int RSetCacheSize) {
55 this.RSetCacheSize = RSetCacheSize;
56 }
57
58 protected void test() throws Exception {
59 ArrayList<String> vmOpts = new ArrayList();
60 Collections.addAll(vmOpts, initialOpts);
61
62 int maxCacheSize = Math.max(0, Math.min(31, getMaxCacheSize()));
63 if (maxCacheSize < RSetCacheSize) {
64 System.out.format("Skiping test for %d cache size due max cache size %d",
65 RSetCacheSize, maxCacheSize
66 );
67 return;
68 }
69
70 printTestInfo(maxCacheSize);
71
72 vmOpts.add("-XX:G1ConcRSLogCacheSize=" + RSetCacheSize);
73 vmOpts.addAll(Arrays.asList(Utils.getTestJavaOpts()));
74
75 // for 32 bits ObjectAlignmentInBytes is not a option
76 if (Platform.is32bit()) {
77 ArrayList<String> vmOptsWithoutAlign = new ArrayList(vmOpts);
78 vmOptsWithoutAlign.add(ShrinkAuxiliaryDataTest.class.getName());
79 performTest(vmOptsWithoutAlign);
80 return;
81 }
82
83 for (int alignment = 3; alignment <= 8; alignment++) {
84 ArrayList<String> vmOptsWithAlign = new ArrayList(vmOpts);
85 vmOptsWithAlign.add("-XX:ObjectAlignmentInBytes="
86 + (int) Math.pow(2, alignment));
87 vmOptsWithAlign.add(ShrinkAuxiliaryDataTest.class.getName());
88
89 performTest(vmOptsWithAlign);
90 }
91 }
92
93 private void performTest(List<String> opts) throws Exception {
94 ProcessBuilder pb
95 = ProcessTools.createJavaProcessBuilder(
96 opts.toArray(new String[opts.size()])
97 );
98
99 OutputAnalyzer output = new OutputAnalyzer(pb.start());
100 output.shouldHaveExitValue(0);
101 }
102
103 private void printTestInfo(int maxCacheSize) {
104
105 DecimalFormat grouped = new DecimalFormat("000,000");
106 DecimalFormatSymbols formatSymbols = grouped.getDecimalFormatSymbols();
107 formatSymbols.setGroupingSeparator(' ');
108 grouped.setDecimalFormatSymbols(formatSymbols);
109
110 System.out.format("Test will use %s bytes of memory of %s available%n"
111 + "Available memory is %s with %d bytes pointer size - can save %s pointers%n"
112 + "Max cache size: 2^%d = %s elements%n",
113 grouped.format(ShrinkAuxiliaryDataTest.getMemoryUsedByTest()),
114 grouped.format(Runtime.getRuntime().freeMemory()),
115 grouped.format(Runtime.getRuntime().freeMemory()
116 - ShrinkAuxiliaryDataTest.getMemoryUsedByTest()),
117 Unsafe.ADDRESS_SIZE,
118 grouped.format((Runtime.getRuntime().freeMemory()
119 - ShrinkAuxiliaryDataTest.getMemoryUsedByTest())
120 / Unsafe.ADDRESS_SIZE),
121 maxCacheSize,
122 grouped.format((int) Math.pow(2, maxCacheSize))
123 );
124 }
125
126 /**
127 * Detects maximum possible size of G1ConcRSLogCacheSize available for
128 * current process based on maximum available process memory size
129 *
130 * @return power of two
131 */
132 private static int getMaxCacheSize() {
133 long availableMemory = Runtime.getRuntime().freeMemory()
134 - ShrinkAuxiliaryDataTest.getMemoryUsedByTest() - 1l;
135 if (availableMemory <= 0) {
136 return 0;
137 }
138 long availablePointersCount = availableMemory / Unsafe.ADDRESS_SIZE;
139 return (63 - (int) Long.numberOfLeadingZeros(availablePointersCount));
140 }
141
142 static class ShrinkAuxiliaryDataTest {
143
144 public static void main(String[] args) throws IOException {
145 int iterateCount = DEFAULT_ITERATION_COUNT;
146
147 if (args.length > 0) {
148 try {
149 iterateCount = Integer.parseInt(args[0]);
150 } catch (NumberFormatException e) {
151 //num_iterate remains default
152 }
153 }
154
155 new ShrinkAuxiliaryDataTest().test(iterateCount);
156 }
157
158 class GarbageObject {
159
160 private final List<byte[]> payload = new ArrayList();
161 private final List<GarbageObject> ref = new LinkedList();
162
163 public GarbageObject(int size) {
164 payload.add(new byte[size]);
165 }
166
167 public void addRef(GarbageObject g) {
168 ref.add(g);
169 }
170
171 public void mutate() {
172 if (!payload.isEmpty() && payload.get(0).length > 0) {
173 payload.get(0)[0] = (byte) (Math.random() * Byte.MAX_VALUE);
174 }
175 }
176 }
177
178 private final List<GarbageObject> garbage = new ArrayList();
179
180 public void test(int num_iterate) throws IOException {
181
182 allocate();
183 link();
184 mutate();
185 deallocate();
186
187 MemoryUsage muBeforeHeap
188 = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
189 MemoryUsage muBeforeNonHeap
190 = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
191
192 for (int i = 0; i < num_iterate; i++) {
193 allocate();
194 link();
195 mutate();
196 deallocate();
197 }
198
199 System.gc();
200 MemoryUsage muAfterHeap
201 = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
202 MemoryUsage muAfterNonHeap
203 = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
204
205 assertLessThanOrEqual(muAfterHeap.getCommitted(), muBeforeHeap.getCommitted(),
206 String.format("heap decommit failed - after > before: %d > %d",
207 muAfterHeap.getCommitted(), muBeforeHeap.getCommitted()
208 )
209 );
210
211 if (muAfterHeap.getCommitted() < muBeforeHeap.getCommitted()) {
212 assertLessThanOrEqual(muAfterNonHeap.getCommitted(), muBeforeNonHeap.getCommitted(),
213 String.format("non-heap decommit failed - after > before: %d > %d",
214 muAfterNonHeap.getCommitted(), muBeforeNonHeap.getCommitted()
215 )
216 );
217 }
218 }
219
220 private void allocate() {
221 for (int r = 0; r < REGIONS_TO_ALLOCATE; r++) {
222 for (int i = 0; i < NUM_OBJECTS_PER_REGION; i++) {
223 GarbageObject g = new GarbageObject(REGION_SIZE
224 / NUM_OBJECTS_PER_REGION);
225 garbage.add(g);
226 }
227 }
228 }
229
230 /**
231 * Iterate through all allocated objects, and link to objects in another
232 * regions
233 */
234 private void link() {
235 for (int ig = 0; ig < garbage.size(); ig++) {
236 int regionNumber = ig / NUM_OBJECTS_PER_REGION;
237
238 for (int i = 0; i < NUM_LINKS; i++) {
239 int regionToLink;
240 do {
241 regionToLink = (int) (Math.random()
242 * REGIONS_TO_ALLOCATE);
243 } while (regionToLink == regionNumber);
244
245 // get random garbage object from random region
246 garbage.get(ig).addRef(garbage.get(regionToLink
247 * NUM_OBJECTS_PER_REGION + (int) (Math.random()
248 * NUM_OBJECTS_PER_REGION)));
249 }
250 }
251 }
252
253 private void mutate() {
254 for (int ig = 0; ig < garbage.size(); ig++) {
255 garbage.get(ig).mutate();
256 }
257 }
258
259 private void deallocate() {
260 garbage.clear();
261 System.gc();
262 }
263
264 static long getMemoryUsedByTest() {
265 return REGIONS_TO_ALLOCATE * REGION_SIZE;
266 }
267
268 private static final int REGION_SIZE = 1024 * 1024;
269 private static final int DEFAULT_ITERATION_COUNT = 1; // iterate main scenario
270 private static final int REGIONS_TO_ALLOCATE = 5;
271 private static final int NUM_OBJECTS_PER_REGION = 10;
272 private static final int NUM_LINKS = 20; // how many links create for each object
273
274 }
275 }
|
1 /*
2 * Copyright (c) 2014, 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.oracle.java.testlibrary.Asserts;
25 import com.oracle.java.testlibrary.OutputAnalyzer;
26 import com.oracle.java.testlibrary.Platform;
27 import com.oracle.java.testlibrary.ProcessTools;
28 import com.oracle.java.testlibrary.Utils;
29 import java.io.IOException;
30 import java.lang.management.ManagementFactory;
31 import java.lang.management.MemoryUsage;
32 import java.text.DecimalFormat;
33 import java.text.DecimalFormatSymbols;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.LinkedList;
38 import java.util.List;
39 import sun.misc.Unsafe; // for ADDRESS_SIZE
40 import sun.hotspot.WhiteBox;
41
42 public class TestShrinkAuxiliaryData {
43
44 private static final int REGION_SIZE = 1024 * 1024;
45
46 private final static String[] initialOpts = new String[]{
47 "-XX:MinHeapFreeRatio=10",
48 "-XX:MaxHeapFreeRatio=11",
49 "-XX:+UseG1GC",
50 "-XX:G1HeapRegionSize=" + REGION_SIZE,
51 "-XX:-ExplicitGCInvokesConcurrent",
52 "-XX:+PrintGCDetails",
53 "-XX:+UnlockDiagnosticVMOptions",
54 "-XX:+WhiteBoxAPI",
55 "-Xbootclasspath/a:.",
56 };
57
58 private final int hotCardTableSize;
59
60 protected TestShrinkAuxiliaryData(int hotCardTableSize) {
61 this.hotCardTableSize = hotCardTableSize;
62 }
63
64 protected void test() throws Exception {
65 ArrayList<String> vmOpts = new ArrayList();
66 Collections.addAll(vmOpts, initialOpts);
67
68 int maxCacheSize = Math.max(0, Math.min(31, getMaxCacheSize()));
69 if (maxCacheSize < hotCardTableSize) {
70 System.out.format("Skiping test for %d cache size due max cache size %d",
71 hotCardTableSize, maxCacheSize
72 );
73 return;
74 }
75
76 printTestInfo(maxCacheSize);
77
78 vmOpts.add("-XX:G1ConcRSLogCacheSize=" + hotCardTableSize);
79 vmOpts.addAll(Arrays.asList(Utils.getTestJavaOpts()));
80
81 // for 32 bits ObjectAlignmentInBytes is not a option
82 if (Platform.is32bit()) {
83 ArrayList<String> vmOptsWithoutAlign = new ArrayList(vmOpts);
84 vmOptsWithoutAlign.add(ShrinkAuxiliaryDataTest.class.getName());
85 performTest(vmOptsWithoutAlign);
86 return;
87 }
88
89 for (int alignment = 3; alignment <= 8; alignment++) {
90 ArrayList<String> vmOptsWithAlign = new ArrayList(vmOpts);
91 vmOptsWithAlign.add("-XX:ObjectAlignmentInBytes="
92 + (int) Math.pow(2, alignment));
93 vmOptsWithAlign.add(ShrinkAuxiliaryDataTest.class.getName());
94
95 performTest(vmOptsWithAlign);
96 }
97 }
98
99 private void performTest(List<String> opts) throws Exception {
100 ProcessBuilder pb
101 = ProcessTools.createJavaProcessBuilder(
102 opts.toArray(new String[opts.size()])
103 );
104
105 OutputAnalyzer output = new OutputAnalyzer(pb.start());
106 output.shouldHaveExitValue(0);
107 System.out.println(output.getOutput());
108 }
109
110 private void printTestInfo(int maxCacheSize) {
111
112 DecimalFormat grouped = new DecimalFormat("000,000");
113 DecimalFormatSymbols formatSymbols = grouped.getDecimalFormatSymbols();
114 formatSymbols.setGroupingSeparator(' ');
115 grouped.setDecimalFormatSymbols(formatSymbols);
116
117 System.out.format(
118 "Test will use %s bytes of memory of %s available%n"
119 + "Available memory is %s with %d bytes pointer size - can save %s pointers%n"
120 + "Max cache size: 2^%d = %s elements%n",
121 grouped.format(ShrinkAuxiliaryDataTest.getMemoryUsedByTest()),
122 grouped.format(Runtime.getRuntime().maxMemory()),
123 grouped.format(Runtime.getRuntime().maxMemory()
124 - ShrinkAuxiliaryDataTest.getMemoryUsedByTest()),
125 Unsafe.ADDRESS_SIZE,
126 grouped.format((Runtime.getRuntime().freeMemory()
127 - ShrinkAuxiliaryDataTest.getMemoryUsedByTest())
128 / Unsafe.ADDRESS_SIZE),
129 maxCacheSize,
130 grouped.format((int) Math.pow(2, maxCacheSize))
131 );
132 }
133
134 /**
135 * Detects maximum possible size of G1ConcRSLogCacheSize available for
136 * current process based on maximum available process memory size
137 *
138 * @return power of two
139 */
140 private static int getMaxCacheSize() {
141 long availableMemory = Runtime.getRuntime().freeMemory()
142 - ShrinkAuxiliaryDataTest.getMemoryUsedByTest() - 1l;
143 if (availableMemory <= 0) {
144 return 0;
145 }
146
147 long availablePointersCount = availableMemory / Unsafe.ADDRESS_SIZE;
148 return (63 - (int) Long.numberOfLeadingZeros(availablePointersCount));
149 }
150
151 static class ShrinkAuxiliaryDataTest {
152
153 public static void main(String[] args) throws IOException {
154
155 ShrinkAuxiliaryDataTest testCase = new ShrinkAuxiliaryDataTest();
156
157 if (!testCase.checkEnvApplicability()) {
158 return;
159 }
160
161 testCase.test();
162 }
163
164 /**
165 * Checks is this environment suitable to run this test
166 * - memory is enough to decommit (page size is not big)
167 * - RSet cache size is not too big
168 *
169 * @return true if test could run, false if test should be skipped
170 */
171 protected boolean checkEnvApplicability() {
172
173 int pageSize = WhiteBox.getWhiteBox().getVMPageSize();
174 System.out.println( "Page size = " + pageSize
175 + " region size = " + REGION_SIZE
176 + " aux data ~= " + (REGION_SIZE * 3 / 100));
177 // If auxdata size will be more that page size it wouldn't decommit.
178 // Auxiliary data size is about ~3.6% of heap size.
179 if (pageSize > REGION_SIZE * 3 / 100) {
180 System.out.format("Skipping test for too large page size = %d",
181 pageSize
182 );
183 return false;
184 }
185
186 if (REGION_SIZE * REGIONS_TO_ALLOCATE > Runtime.getRuntime().maxMemory()) {
187 System.out.format("Skipping test for too low available memory. "
188 + "Need %d, available %d",
189 REGION_SIZE * REGIONS_TO_ALLOCATE,
190 Runtime.getRuntime().maxMemory()
191 );
192 return false;
193 }
194
195 return true;
196 }
197
198 class GarbageObject {
199
200 private final List<byte[]> payload = new ArrayList();
201 private final List<GarbageObject> ref = new LinkedList();
202
203 public GarbageObject(int size) {
204 payload.add(new byte[size]);
205 }
206
207 public void addRef(GarbageObject g) {
208 ref.add(g);
209 }
210
211 public void mutate() {
212 if (!payload.isEmpty() && payload.get(0).length > 0) {
213 payload.get(0)[0] = (byte) (Math.random() * Byte.MAX_VALUE);
214 }
215 }
216 }
217
218 private final List<GarbageObject> garbage = new ArrayList();
219
220 public void test() throws IOException {
221
222 MemoryUsage muFull, muFree, muAuxDataFull, muAuxDataFree;
223 float auxFull, auxFree;
224
225 allocate();
226 link();
227 mutate();
228
229 muFull = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
230 long numUsedRegions = WhiteBox.getWhiteBox().g1NumMaxRegions()
231 - WhiteBox.getWhiteBox().g1NumFreeRegions();
232 muAuxDataFull = WhiteBox.getWhiteBox().g1AuxiliaryMemoryUsage();
233 auxFull = (float)muAuxDataFull.getUsed() / numUsedRegions;
234
235 System.out.format("Full aux data ratio= %f, regions max= %d, used= %d\n",
236 auxFull, WhiteBox.getWhiteBox().g1NumMaxRegions(), numUsedRegions
237 );
238
239 deallocate();
240 System.gc();
241
242 muFree = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
243 muAuxDataFree = WhiteBox.getWhiteBox().g1AuxiliaryMemoryUsage();
244
245 numUsedRegions = WhiteBox.getWhiteBox().g1NumMaxRegions()
246 - WhiteBox.getWhiteBox().g1NumFreeRegions();
247 auxFree = (float)muAuxDataFree.getUsed() / numUsedRegions;
248
249 System.out.format("Free aux data ratio= %f, regions max= %d, used= %d\n",
250 auxFree, WhiteBox.getWhiteBox().g1NumMaxRegions(), numUsedRegions
251 );
252
253 Asserts.assertLessThanOrEqual(muFree.getCommitted(), muFull.getCommitted(),
254 String.format("heap decommit failed - full > free: %d > %d",
255 muFree.getCommitted(), muFull.getCommitted()
256 )
257 );
258
259 System.out.format("State used committed\n");
260 System.out.format("Full aux data: %10d %10d\n", muAuxDataFull.getUsed(), muAuxDataFull.getCommitted());
261 System.out.format("Free aux data: %10d %10d\n", muAuxDataFree.getUsed(), muAuxDataFree.getCommitted());
262
263 // if decommited check that aux data has same ratio
264 if (muFree.getCommitted() < muFull.getCommitted()) {
265 Asserts.assertLessThanOrEqual(auxFree, auxFull,
266 String.format("auxiliary data decommit failed - full > free: %f > %f",
267 auxFree, auxFull
268 )
269 );
270 }
271 }
272
273 private void allocate() {
274 for (int r = 0; r < REGIONS_TO_ALLOCATE; r++) {
275 for (int i = 0; i < NUM_OBJECTS_PER_REGION; i++) {
276 GarbageObject g = new GarbageObject(REGION_SIZE
277 / NUM_OBJECTS_PER_REGION);
278 garbage.add(g);
279 }
280 }
281 }
282
283 /**
284 * Iterate through all allocated objects, and link to objects in another
285 * regions
286 */
287 private void link() {
288 for (int ig = 0; ig < garbage.size(); ig++) {
289 int regionNumber = ig / NUM_OBJECTS_PER_REGION;
290
291 for (int i = 0; i < NUM_LINKS; i++) {
292 int regionToLink;
293 do {
294 regionToLink = (int) (Math.random() * REGIONS_TO_ALLOCATE);
295 } while (regionToLink == regionNumber);
296
297 // get random garbage object from random region
298 garbage.get(ig).addRef(garbage.get(regionToLink
299 * NUM_OBJECTS_PER_REGION + (int) (Math.random()
300 * NUM_OBJECTS_PER_REGION)));
301 }
302 }
303 }
304
305 private void mutate() {
306 for (int ig = 0; ig < garbage.size(); ig++) {
307 garbage.get(ig).mutate();
308 }
309 }
310
311 private void deallocate() {
312 garbage.clear();
313 System.gc();
314 }
315
316 static long getMemoryUsedByTest() {
317 return REGIONS_TO_ALLOCATE * REGION_SIZE;
318 }
319
320 private static final int REGIONS_TO_ALLOCATE = 100;
321 private static final int NUM_OBJECTS_PER_REGION = 10;
322 private static final int NUM_LINKS = 20; // how many links create for each object
323 }
324 }
|