Mercurial > hg > graal-compiler
comparison test/gc/survivorAlignment/SurvivorAlignmentTestMain.java @ 20717:4b7c96fba3d8
8037968: Add tests on alignment of objects copied to survivor space
Reviewed-by: jmasa, dfazunen
author | fzhinkin |
---|---|
date | Wed, 26 Nov 2014 14:17:06 +0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
20716:7e70976b4d0f | 20717:4b7c96fba3d8 |
---|---|
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 java.lang.management.ManagementFactory; | |
25 import java.lang.management.MemoryPoolMXBean; | |
26 import java.util.Objects; | |
27 import java.util.Optional; | |
28 import java.util.regex.Matcher; | |
29 import java.util.regex.Pattern; | |
30 | |
31 import com.oracle.java.testlibrary.Asserts; | |
32 import com.sun.management.ThreadMXBean; | |
33 import sun.hotspot.WhiteBox; | |
34 import sun.misc.Unsafe; | |
35 | |
36 /** | |
37 * Main class for tests on {@code SurvivorAlignmentInBytes} option. | |
38 * | |
39 * Typical usage is to obtain instance using fromArgs method, allocate objects | |
40 * and verify that actual memory usage in tested heap space is close to | |
41 * expected. | |
42 */ | |
43 public class SurvivorAlignmentTestMain { | |
44 enum HeapSpace { | |
45 EDEN, | |
46 SURVIVOR, | |
47 TENURED | |
48 } | |
49 | |
50 public static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); | |
51 | |
52 public static final long MAX_TENURING_THRESHOLD = Optional.ofNullable( | |
53 SurvivorAlignmentTestMain.WHITE_BOX.getIntxVMFlag( | |
54 "MaxTenuringThreshold")).orElse(15L); | |
55 | |
56 /** | |
57 * Regexp used to parse memory size params, like 2G, 34m or 15k. | |
58 */ | |
59 private static final Pattern SIZE_REGEX | |
60 = Pattern.compile("(?<size>[0-9]+)(?<multiplier>[GMKgmk])?"); | |
61 | |
62 // Names of different heap spaces. | |
63 private static final String DEF_NEW_EDEN = "Eden Space"; | |
64 private static final String DEF_NEW_SURVIVOR = "Survivor Space"; | |
65 private static final String PAR_NEW_EDEN = "Par Eden Space"; | |
66 private static final String PAR_NEW_SURVIVOR = "Par Survivor Space"; | |
67 private static final String PS_EDEN = "PS Eden Space"; | |
68 private static final String PS_SURVIVOR = "PS Survivor Space"; | |
69 private static final String G1_EDEN = "G1 Eden Space"; | |
70 private static final String G1_SURVIVOR = "G1 Survivor Space"; | |
71 private static final String SERIAL_TENURED = "Tenured Gen"; | |
72 private static final String CMS_TENURED = "CMS Old Gen"; | |
73 private static final String PS_TENURED = "PS Old Gen"; | |
74 private static final String G1_TENURED = "G1 Old Gen"; | |
75 | |
76 private static final long G1_HEAP_REGION_SIZE = Optional.ofNullable( | |
77 SurvivorAlignmentTestMain.WHITE_BOX.getUintxVMFlag( | |
78 "G1HeapRegionSize")).orElse(-1L); | |
79 | |
80 /** | |
81 * Min size of free chunk in CMS generation. | |
82 * An object allocated in CMS generation will at least occupy this amount | |
83 * of bytes. | |
84 */ | |
85 private static final long CMS_MIN_FREE_CHUNK_SIZE | |
86 = 3L * Unsafe.ADDRESS_SIZE; | |
87 | |
88 private static final AlignmentHelper EDEN_SPACE_HELPER; | |
89 private static final AlignmentHelper SURVIVOR_SPACE_HELPER; | |
90 private static final AlignmentHelper TENURED_SPACE_HELPER; | |
91 /** | |
92 * Amount of memory that should be filled during a test run. | |
93 */ | |
94 private final long memoryToFill; | |
95 /** | |
96 * The size of an objects that will be allocated during a test run. | |
97 */ | |
98 private final long objectSize; | |
99 /** | |
100 * Amount of memory that will be actually occupied by an object in eden | |
101 * space. | |
102 */ | |
103 private final long actualObjectSize; | |
104 /** | |
105 * Storage for allocated objects. | |
106 */ | |
107 private final Object[] garbage; | |
108 /** | |
109 * Heap space whose memory usage is a subject of assertions during the test | |
110 * run. | |
111 */ | |
112 private final HeapSpace testedSpace; | |
113 | |
114 private long[] baselinedThreadMemoryUsage = null; | |
115 private long[] threadIds = null; | |
116 | |
117 /** | |
118 * Initialize {@code EDEN_SPACE_HELPER}, {@code SURVIVOR_SPACE_HELPER} and | |
119 * {@code TENURED_SPACE_HELPER} to represent heap spaces in use. | |
120 * | |
121 * Note that regardless to GC object's alignment in survivor space is | |
122 * expected to be equal to {@code SurvivorAlignmentInBytes} value and | |
123 * alignment in other spaces is expected to be equal to | |
124 * {@code ObjectAlignmentInBytes} value. | |
125 * | |
126 * In CMS generation we can't allocate less then {@code MinFreeChunk} value, | |
127 * for other CGs we expect that object of size {@code MIN_OBJECT_SIZE} | |
128 * could be allocated as it is (of course, its size could be aligned | |
129 * according to alignment value used in a particular space). | |
130 * | |
131 * For G1 GC MXBeans could report memory usage only with region size | |
132 * precision (if an object allocated in some G1 heap region, then all region | |
133 * will claimed as used), so for G1's spaces precision is equal to | |
134 * {@code G1HeapRegionSize} value. | |
135 */ | |
136 static { | |
137 AlignmentHelper edenHelper = null; | |
138 AlignmentHelper survivorHelper = null; | |
139 AlignmentHelper tenuredHelper = null; | |
140 for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) { | |
141 switch (pool.getName()) { | |
142 case SurvivorAlignmentTestMain.DEF_NEW_EDEN: | |
143 case SurvivorAlignmentTestMain.PAR_NEW_EDEN: | |
144 case SurvivorAlignmentTestMain.PS_EDEN: | |
145 Asserts.assertNull(edenHelper, | |
146 "Only one bean for eden space is expected."); | |
147 edenHelper = new AlignmentHelper( | |
148 AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES, | |
149 AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES, | |
150 AlignmentHelper.MIN_OBJECT_SIZE, pool); | |
151 break; | |
152 case SurvivorAlignmentTestMain.G1_EDEN: | |
153 Asserts.assertNull(edenHelper, | |
154 "Only one bean for eden space is expected."); | |
155 edenHelper = new AlignmentHelper( | |
156 SurvivorAlignmentTestMain.G1_HEAP_REGION_SIZE, | |
157 AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES, | |
158 AlignmentHelper.MIN_OBJECT_SIZE, pool); | |
159 break; | |
160 case SurvivorAlignmentTestMain.DEF_NEW_SURVIVOR: | |
161 case SurvivorAlignmentTestMain.PAR_NEW_SURVIVOR: | |
162 case SurvivorAlignmentTestMain.PS_SURVIVOR: | |
163 Asserts.assertNull(survivorHelper, | |
164 "Only one bean for survivor space is expected."); | |
165 survivorHelper = new AlignmentHelper( | |
166 AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES, | |
167 AlignmentHelper.SURVIVOR_ALIGNMENT_IN_BYTES, | |
168 AlignmentHelper.MIN_OBJECT_SIZE, pool); | |
169 break; | |
170 case SurvivorAlignmentTestMain.G1_SURVIVOR: | |
171 Asserts.assertNull(survivorHelper, | |
172 "Only one bean for survivor space is expected."); | |
173 survivorHelper = new AlignmentHelper( | |
174 SurvivorAlignmentTestMain.G1_HEAP_REGION_SIZE, | |
175 AlignmentHelper.SURVIVOR_ALIGNMENT_IN_BYTES, | |
176 AlignmentHelper.MIN_OBJECT_SIZE, pool); | |
177 break; | |
178 case SurvivorAlignmentTestMain.SERIAL_TENURED: | |
179 case SurvivorAlignmentTestMain.PS_TENURED: | |
180 case SurvivorAlignmentTestMain.G1_TENURED: | |
181 Asserts.assertNull(tenuredHelper, | |
182 "Only one bean for tenured space is expected."); | |
183 tenuredHelper = new AlignmentHelper( | |
184 AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES, | |
185 AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES, | |
186 AlignmentHelper.MIN_OBJECT_SIZE, pool); | |
187 break; | |
188 case SurvivorAlignmentTestMain.CMS_TENURED: | |
189 Asserts.assertNull(tenuredHelper, | |
190 "Only one bean for tenured space is expected."); | |
191 tenuredHelper = new AlignmentHelper( | |
192 AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES, | |
193 AlignmentHelper.OBJECT_ALIGNMENT_IN_BYTES, | |
194 SurvivorAlignmentTestMain.CMS_MIN_FREE_CHUNK_SIZE, | |
195 pool); | |
196 break; | |
197 } | |
198 } | |
199 EDEN_SPACE_HELPER = Objects.requireNonNull(edenHelper, | |
200 "AlignmentHelper for eden space should be initialized."); | |
201 SURVIVOR_SPACE_HELPER = Objects.requireNonNull(survivorHelper, | |
202 "AlignmentHelper for survivor space should be initialized."); | |
203 TENURED_SPACE_HELPER = Objects.requireNonNull(tenuredHelper, | |
204 "AlignmentHelper for tenured space should be initialized."); | |
205 } | |
206 /** | |
207 * Returns an SurvivorAlignmentTestMain instance constructed using CLI | |
208 * options. | |
209 * | |
210 * Following options are expected: | |
211 * <ul> | |
212 * <li>memoryToFill</li> | |
213 * <li>objectSize</li> | |
214 * </ul> | |
215 * | |
216 * Both argument may contain multiplier suffix k, m or g. | |
217 */ | |
218 public static SurvivorAlignmentTestMain fromArgs(String[] args) { | |
219 Asserts.assertEQ(args.length, 3, "Expected three arguments: " | |
220 + "memory size, object size and tested heap space name."); | |
221 | |
222 long memoryToFill = parseSize(args[0]); | |
223 long objectSize = Math.max(parseSize(args[1]), | |
224 AlignmentHelper.MIN_ARRAY_SIZE); | |
225 HeapSpace testedSpace = HeapSpace.valueOf(args[2]); | |
226 | |
227 return new SurvivorAlignmentTestMain(memoryToFill, objectSize, | |
228 testedSpace); | |
229 } | |
230 | |
231 /** | |
232 * Returns a value parsed from a string with format | |
233 * <integer><multiplier>. | |
234 */ | |
235 private static long parseSize(String sizeString) { | |
236 Matcher matcher = SIZE_REGEX.matcher(sizeString); | |
237 Asserts.assertTrue(matcher.matches(), | |
238 "sizeString should have following format \"[0-9]+([MBK])?\""); | |
239 long size = Long.valueOf(matcher.group("size")); | |
240 | |
241 if (matcher.group("multiplier") != null) { | |
242 long K = 1024L; | |
243 // fall through multipliers | |
244 switch (matcher.group("multiplier").toLowerCase()) { | |
245 case "g": | |
246 size *= K; | |
247 case "m": | |
248 size *= K; | |
249 case "k": | |
250 size *= K; | |
251 } | |
252 } | |
253 return size; | |
254 } | |
255 | |
256 private SurvivorAlignmentTestMain(long memoryToFill, long objectSize, | |
257 HeapSpace testedSpace) { | |
258 this.objectSize = objectSize; | |
259 this.memoryToFill = memoryToFill; | |
260 this.testedSpace = testedSpace; | |
261 | |
262 AlignmentHelper helper = SurvivorAlignmentTestMain.EDEN_SPACE_HELPER; | |
263 | |
264 this.actualObjectSize = helper.getObjectSizeInThisSpace( | |
265 this.objectSize); | |
266 int arrayLength = helper.getObjectsCount(memoryToFill, this.objectSize); | |
267 garbage = new Object[arrayLength]; | |
268 } | |
269 | |
270 /** | |
271 * Allocate byte arrays to fill {@code memoryToFill} memory. | |
272 */ | |
273 public void allocate() { | |
274 int byteArrayLength = Math.max((int) (objectSize | |
275 - Unsafe.ARRAY_BYTE_BASE_OFFSET), 0); | |
276 | |
277 for (int i = 0; i < garbage.length; i++) { | |
278 garbage[i] = new byte[byteArrayLength]; | |
279 } | |
280 } | |
281 | |
282 /** | |
283 * Release memory occupied after {@code allocate} call. | |
284 */ | |
285 public void release() { | |
286 for (int i = 0; i < garbage.length; i++) { | |
287 garbage[i] = null; | |
288 } | |
289 } | |
290 | |
291 /** | |
292 * Returns expected amount of memory occupied in a {@code heapSpace} by | |
293 * objects referenced from {@code garbage} array. | |
294 */ | |
295 public long getExpectedMemoryUsage() { | |
296 AlignmentHelper alignmentHelper = getAlignmentHelper(testedSpace); | |
297 return alignmentHelper.getExpectedMemoryUsage(objectSize, | |
298 garbage.length); | |
299 } | |
300 | |
301 /** | |
302 * Verifies that memory usage in a {@code heapSpace} deviates from | |
303 * {@code expectedUsage} for no more than {@code MAX_RELATIVE_DEVIATION}. | |
304 */ | |
305 public void verifyMemoryUsage(long expectedUsage) { | |
306 AlignmentHelper alignmentHelper = getAlignmentHelper(testedSpace); | |
307 | |
308 long actualMemoryUsage = alignmentHelper.getActualMemoryUsage(); | |
309 boolean otherThreadsAllocatedMemory = areOtherThreadsAllocatedMemory(); | |
310 | |
311 long memoryUsageDiff = Math.abs(actualMemoryUsage - expectedUsage); | |
312 long maxAllowedUsageDiff | |
313 = alignmentHelper.getAllowedMemoryUsageDeviation(expectedUsage); | |
314 | |
315 System.out.println("Verifying memory usage in space: " + testedSpace); | |
316 System.out.println("Allocated objects count: " + garbage.length); | |
317 System.out.println("Desired object size: " + objectSize); | |
318 System.out.println("Actual object size: " + actualObjectSize); | |
319 System.out.println("Expected object size in space: " | |
320 + alignmentHelper.getObjectSizeInThisSpace(objectSize)); | |
321 System.out.println("Expected memory usage: " + expectedUsage); | |
322 System.out.println("Actual memory usage: " + actualMemoryUsage); | |
323 System.out.println("Memory usage diff: " + memoryUsageDiff); | |
324 System.out.println("Max allowed usage diff: " + maxAllowedUsageDiff); | |
325 | |
326 if (memoryUsageDiff > maxAllowedUsageDiff | |
327 && otherThreadsAllocatedMemory) { | |
328 System.out.println("Memory usage diff is incorrect, but it seems " | |
329 + "like someone else allocated objects"); | |
330 return; | |
331 } | |
332 | |
333 Asserts.assertLTE(memoryUsageDiff, maxAllowedUsageDiff, | |
334 "Actual memory usage should not deviate from expected for " + | |
335 "more then " + maxAllowedUsageDiff); | |
336 } | |
337 | |
338 /** | |
339 * Baselines amount of memory allocated by each thread. | |
340 */ | |
341 public void baselineMemoryAllocation() { | |
342 ThreadMXBean bean = (ThreadMXBean) ManagementFactory.getThreadMXBean(); | |
343 threadIds = bean.getAllThreadIds(); | |
344 baselinedThreadMemoryUsage = bean.getThreadAllocatedBytes(threadIds); | |
345 } | |
346 | |
347 /** | |
348 * Checks if threads other then the current thread were allocating objects | |
349 * after baselinedThreadMemoryUsage call. | |
350 * | |
351 * If baselinedThreadMemoryUsage was not called, then this method will return | |
352 * {@code false}. | |
353 */ | |
354 public boolean areOtherThreadsAllocatedMemory() { | |
355 if (baselinedThreadMemoryUsage == null) { | |
356 return false; | |
357 } | |
358 | |
359 ThreadMXBean bean = (ThreadMXBean) ManagementFactory.getThreadMXBean(); | |
360 long currentMemoryAllocation[] | |
361 = bean.getThreadAllocatedBytes(threadIds); | |
362 boolean otherThreadsAllocatedMemory = false; | |
363 | |
364 System.out.println("Verifying amount of memory allocated by threads:"); | |
365 for (int i = 0; i < threadIds.length; i++) { | |
366 System.out.format("Thread %d%nbaseline allocation: %d" | |
367 + "%ncurrent allocation:%d%n", threadIds[i], | |
368 baselinedThreadMemoryUsage[i], currentMemoryAllocation[i]); | |
369 System.out.println(bean.getThreadInfo(threadIds[i])); | |
370 | |
371 long bytesAllocated = Math.abs(currentMemoryAllocation[i] | |
372 - baselinedThreadMemoryUsage[i]); | |
373 if (bytesAllocated > 0 | |
374 && threadIds[i] != Thread.currentThread().getId()) { | |
375 otherThreadsAllocatedMemory = true; | |
376 } | |
377 } | |
378 | |
379 return otherThreadsAllocatedMemory; | |
380 } | |
381 | |
382 @Override | |
383 public String toString() { | |
384 StringBuilder builder = new StringBuilder(); | |
385 | |
386 builder.append(String.format("SurvivorAlignmentTestMain info:%n")); | |
387 builder.append(String.format("Desired object size: %d%n", objectSize)); | |
388 builder.append(String.format("Memory to fill: %d%n", memoryToFill)); | |
389 builder.append(String.format("Objects to be allocated: %d%n", | |
390 garbage.length)); | |
391 | |
392 builder.append(String.format("Alignment helpers to be used: %n")); | |
393 for (HeapSpace heapSpace: HeapSpace.values()) { | |
394 builder.append(String.format("For space %s:%n%s%n", heapSpace, | |
395 getAlignmentHelper(heapSpace))); | |
396 } | |
397 | |
398 return builder.toString(); | |
399 } | |
400 | |
401 /** | |
402 * Returns {@code AlignmentHelper} for a space {@code heapSpace}. | |
403 */ | |
404 public static AlignmentHelper getAlignmentHelper(HeapSpace heapSpace) { | |
405 switch (heapSpace) { | |
406 case EDEN: | |
407 return SurvivorAlignmentTestMain.EDEN_SPACE_HELPER; | |
408 case SURVIVOR: | |
409 return SurvivorAlignmentTestMain.SURVIVOR_SPACE_HELPER; | |
410 case TENURED: | |
411 return SurvivorAlignmentTestMain.TENURED_SPACE_HELPER; | |
412 default: | |
413 throw new Error("Unexpected heap space: " + heapSpace); | |
414 } | |
415 } | |
416 } |