1 /*
2 * Copyright (c) 2016, 2021, 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 /*
25 * @test TestPLABPromotion
26 * @bug 8141278 8141141
27 * @summary Test PLAB promotion
28 * @requires vm.gc.G1
29 * @requires !vm.flightRecorder
30 * @library /test/lib /
31 * @modules java.base/jdk.internal.misc
32 * @modules java.management
33 * @build sun.hotspot.WhiteBox
34 * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
35 * @run main/timeout=240 gc.g1.plab.TestPLABPromotion
36 */
37 package gc.g1.plab;
38
39 import java.util.List;
40 import java.util.Arrays;
41 import java.io.PrintStream;
42
43 import gc.g1.plab.lib.AppPLABPromotion;
44 import gc.g1.plab.lib.LogParser;
45 import gc.g1.plab.lib.PLABUtils;
46 import gc.g1.plab.lib.PlabInfo;
47
48 import jdk.test.lib.process.OutputAnalyzer;
49 import jdk.test.lib.process.ProcessTools;
50
51 /**
52 * Test checks PLAB promotion of different size objects.
53 */
54 public class TestPLABPromotion {
55
56 // GC ID with survivor PLAB statistics
57 private final static long GC_ID_SURVIVOR_STATS = 1l;
58 // GC ID with old PLAB statistics
59 private final static long GC_ID_OLD_STATS = 2l;
60
61 private final static String PLAB_USED_FIELD_NAME = "used";
62 private final static String PLAB_DIRECT_ALLOCATED_FIELD_NAME = "direct allocated";
63 private final static List<String> FIELDS_TO_EXTRACT = Arrays.asList(PLAB_USED_FIELD_NAME, PLAB_DIRECT_ALLOCATED_FIELD_NAME);
64
65 private static String output;
66
67 // Allowable difference for memory consumption (percentage)
68 private final static long MEM_DIFFERENCE_PCT = 5;
69
70 private static final int PLAB_SIZE_SMALL = 1024;
71 private static final int PLAB_SIZE_MEDIUM = 4096;
72 private static final int PLAB_SIZE_HIGH = 65536;
73 private static final int OBJECT_SIZE_SMALL = 10;
74 private static final int OBJECT_SIZE_MEDIUM = 100;
75 private static final int OBJECT_SIZE_HIGH = 3250;
76 private static final int GC_NUM_SMALL = 1;
77 private static final int GC_NUM_MEDIUM = 3;
78 private static final int GC_NUM_HIGH = 7;
79 private static final int WASTE_PCT_SMALL = 10;
80 private static final int WASTE_PCT_MEDIUM = 20;
81 private static final int WASTE_PCT_HIGH = 30;
82 private static final int YOUNG_SIZE_LOW = 16;
83 private static final int YOUNG_SIZE_HIGH = 64;
84 private static final boolean PLAB_FIXED = true;
85 private static final boolean PLAB_DYNAMIC = false;
86
87 private final static TestCase[] TEST_CASES = {
88 // Test cases for unreachable object, PLAB size is fixed
89 new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, false, false),
90 new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, false, false),
91 // Test cases for reachable objects, PLAB size is fixed
92 new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, true, true),
93 new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_MEDIUM, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, true, true),
94 new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, PLAB_FIXED, true, false),
95 new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, true, true),
96 new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, true, true),
97 new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, PLAB_FIXED, true, true),
98 new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_SMALL, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, true, true),
99 new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, true, true),
100 new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_HIGH, PLAB_FIXED, true, false),
101 // Test cases for unreachable object, PLAB size is not fixed
102 new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_LOW, PLAB_DYNAMIC, false, false),
103 // Test cases for reachable objects, PLAB size is not fixed
104 new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_DYNAMIC, true, true),
105 new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_DYNAMIC, true, true),
106 new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_DYNAMIC, true, false),
107 new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, PLAB_DYNAMIC, true, true),
108 new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_HIGH, PLAB_DYNAMIC, true, true),
109 new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_LOW, PLAB_DYNAMIC, true, true)
110 };
111
112 public static void main(String[] args) throws Throwable {
113
114 for (TestCase testCase : TEST_CASES) {
115 // What we going to check.
116 testCase.print(System.out);
117 List<String> options = PLABUtils.prepareOptions(testCase.toOptions());
118 options.add(AppPLABPromotion.class.getName());
119 OutputAnalyzer out = ProcessTools.executeTestJvm(options);
120 PLABUtils.commonCheck(out);
121 output = out.getOutput();
122 checkResults(testCase);
123 }
124 }
125
126 private static void checkResults(TestCase testCase) {
127 long plabAllocatedSurvivor;
128 long directAllocatedSurvivor;
129 long plabAllocatedOld;
130 long directAllocatedOld;
131 long memAllocated = testCase.getMemToFill();
132 LogParser logParser = new LogParser(output);
133
134 PlabInfo survivorPlabInfo = logParser.getSpecifiedStats(GC_ID_SURVIVOR_STATS, LogParser.ReportType.SURVIVOR_STATS, FIELDS_TO_EXTRACT);
135 PlabInfo oldPlabInfo = logParser.getSpecifiedStats(GC_ID_OLD_STATS, LogParser.ReportType.OLD_STATS, FIELDS_TO_EXTRACT);
136
137 checkFields(survivorPlabInfo);
138 checkFields(oldPlabInfo);
139
140 plabAllocatedSurvivor = survivorPlabInfo.get(PLAB_USED_FIELD_NAME);
141 directAllocatedSurvivor = survivorPlabInfo.get(PLAB_DIRECT_ALLOCATED_FIELD_NAME);
142 plabAllocatedOld = oldPlabInfo.get(PLAB_USED_FIELD_NAME);
143 directAllocatedOld = oldPlabInfo.get(PLAB_DIRECT_ALLOCATED_FIELD_NAME);
144
145 System.out.printf("Survivor PLAB allocated:%17d Direct allocated: %17d Mem consumed:%17d%n", plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated);
146 System.out.printf("Old PLAB allocated:%17d Direct allocated: %17d Mem consumed:%17d%n", plabAllocatedOld, directAllocatedOld, memAllocated);
147
148 // Unreachable objects case
149 if (testCase.isDeadObjectCase()) {
150 checkDeadObjectsPromotion(plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated);
151 checkDeadObjectsPromotion(plabAllocatedOld, directAllocatedOld, memAllocated);
152
153 } else {
154 // Live objects case
155 if (testCase.isPromotedByPLAB()) {
156 checkLiveObjectsPromotion(plabAllocatedSurvivor, memAllocated, "Expect that Survivor PLAB allocation are similar to all mem consumed");
157 checkLiveObjectsPromotion(plabAllocatedOld, memAllocated, "Expect that Old PLAB allocation are similar to all mem consumed");
158 } else {
159 // All big objects should be directly allocated
160 checkLiveObjectsPromotion(directAllocatedSurvivor, memAllocated, "Expect that Survivor direct allocation are similar to all mem consumed");
161 checkLiveObjectsPromotion(directAllocatedOld, memAllocated, "Expect that Old direct allocation are similar to all mem consumed");
162 }
163
164 checkTotalPromotion(plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated, "Expect that Survivor gen total allocation are similar to all mem consumed");
165 checkTotalPromotion(plabAllocatedOld, directAllocatedOld, memAllocated, "Expect that Old gen total allocation are similar to all mem consumed");
166 }
167 System.out.println("Test passed!");
168 }
169
170 private static void checkTotalPromotion(long plabAllocatedSurvivor, long directAllocatedSurvivor, long memAllocated, String exceptionMessage) {
171 // All promoted objects size should be similar to all consumed memory
172 if (!checkDifferenceRatio(plabAllocatedSurvivor + directAllocatedSurvivor, memAllocated)) {
173 System.out.println(output);
174 throw new RuntimeException(exceptionMessage);
175 }
176 }
177
178 /**
179 * Checks that live objects were promoted as expected.
180 * @param plabAllocated
181 * @param totalMemAllocated
182 * @param exceptionMessage
183 */
184 private static void checkLiveObjectsPromotion(long plabAllocated, long totalMemAllocated, String exceptionMessage) {
185 // All live small objects should be promoted using PLAB
186 if (!checkDifferenceRatio(plabAllocated, totalMemAllocated)) {
187 System.out.println(output);
188 throw new RuntimeException(exceptionMessage);
189 }
190 }
191
192 /**
193 * Checks that dead objects are not promoted.
194 * @param plabPromoted promoted by PLAB
195 * @param directlyPromoted
196 * @param memoryAllocated total memory allocated
197 */
198 private static void checkDeadObjectsPromotion(long plabPromoted, long directlyPromoted, long memoryAllocated) {
199 // No dead objects should be promoted
200 if (!(checkRatio(plabPromoted, memoryAllocated) && checkRatio(directlyPromoted, memoryAllocated))) {
201 System.out.println(output);
202 throw new RuntimeException("Unreachable objects should not be allocated using PLAB or directly allocated to Survivor/Old");
203 }
204 }
205
206 /**
207 * Checks that PLAB statistics contains expected fields.
208 * @param info
209 */
210 private static void checkFields(PlabInfo info) {
211 if (!info.checkFields(FIELDS_TO_EXTRACT)) {
212 System.out.println(output);
213 throw new RuntimeException("PLAB log does not contain expected fields");
214 }
215 }
216
217 /**
218 * Returns true if checkedValue is less than MEM_DIFFERENCE_PCT percent of controlValue.
219 *
220 * @param checkedValue - checked value
221 * @param controlValue - referent value
222 * @return true if checkedValue is less than MEM_DIFFERENCE_PCT percent of controlValue
223 */
224 private static boolean checkRatio(long checkedValue, long controlValue) {
225 return Math.abs(checkedValue * 100.0 / controlValue) < MEM_DIFFERENCE_PCT;
226 }
227
228 /**
229 * Returns true if difference of checkedValue and controlValue is less than
230 * MEM_DIFFERENCE_PCT percent of controlValue.
231 *
232 * @param checkedValue - checked value
233 * @param controlValue - referent value
234 * @return true if difference of checkedValue and controlValue is less than
235 * MEM_DIFFERENCE_PCT percent of controlValue
236 */
237 private static boolean checkDifferenceRatio(long checkedValue, long controlValue) {
238 return Math.abs((checkedValue - controlValue) * 100.0 / controlValue) < MEM_DIFFERENCE_PCT;
239 }
240
241 /**
242 * Description of one test case.
243 */
244 private static class TestCase {
245
246 private final int wastePct;
247 private final int plabSize;
248 private final int chunkSize;
249 private final int parGCThreads;
250 private final int edenSize;
251 private final boolean plabIsFixed;
252 private final boolean objectsAreReachable;
253 private final boolean promotedByPLAB;
254
255 /**
256 * @param wastePct
257 * ParallelGCBufferWastePct
258 * @param plabSize
259 * -XX:OldPLABSize and -XX:YoungPLABSize
260 * @param chunkSize
261 * requested object size for memory consumption
262 * @param parGCThreads
263 * -XX:ParallelGCThreads
264 * @param edenSize
265 * NewSize and MaxNewSize
266 * @param plabIsFixed
267 * Use dynamic PLAB or fixed size PLAB
268 * @param objectsAreReachable
269 * true - allocate live objects
270 * false - allocate unreachable objects
271 * @param promotedByPLAB
272 * true - we expect to see PLAB allocation during promotion
273 * false - objects will be directly allocated during promotion
274 */
275 public TestCase(int wastePct,
276 int plabSize,
277 int chunkSize,
278 int parGCThreads,
279 int edenSize,
280 boolean plabIsFixed,
281 boolean objectsAreReachable,
282 boolean promotedByPLAB
283 ) {
284 if (wastePct == 0 || plabSize == 0 || chunkSize == 0 || parGCThreads == 0 || edenSize == 0) {
285 throw new IllegalArgumentException("Parameters should not be 0");
286 }
287 this.wastePct = wastePct;
288 this.plabSize = plabSize;
289 this.chunkSize = chunkSize;
290 this.parGCThreads = parGCThreads;
291 this.edenSize = edenSize;
292 this.plabIsFixed = plabIsFixed;
293 this.objectsAreReachable = objectsAreReachable;
294 this.promotedByPLAB = promotedByPLAB;
295 }
296
297 /**
298 * Convert current TestCase to List of options.
299 * Assume test will fill half of existed eden.
300 *
301 * @return
302 * List of options
303 */
304 public List<String> toOptions() {
305 return Arrays.asList("-XX:ParallelGCThreads=" + parGCThreads,
306 "-XX:ParallelGCBufferWastePct=" + wastePct,
307 "-XX:ParallelGCThreads=1", // Avoid dynamic sizing of threads.
308 "-XX:OldPLABSize=" + plabSize,
309 "-XX:YoungPLABSize=" + plabSize,
310 "-XX:" + (plabIsFixed ? "-" : "+") + "ResizePLAB",
311 "-Dchunk.size=" + chunkSize,
312 "-Dreachable=" + objectsAreReachable,
313 "-XX:NewSize=" + edenSize + "m",
314 "-XX:MaxNewSize=" + edenSize + "m",
315 "-Dmem.to.fill=" + getMemToFill()
316 );
317 }
318
319 /**
320 * Print details about test case.
321 */
322 public void print(PrintStream out) {
323 boolean expectPLABAllocation = promotedByPLAB && objectsAreReachable;
324 boolean expectDirectAllocation = (!promotedByPLAB) && objectsAreReachable;
325
326 out.println("Test case details:");
327 out.println(" Young gen size : " + edenSize + "M");
328 out.println(" Predefined PLAB size : " + plabSize);
329 out.println(" Parallel GC buffer waste pct : " + wastePct);
330 out.println(" Chunk size : " + chunkSize);
331 out.println(" Parallel GC threads : " + parGCThreads);
332 out.println(" Objects are created : " + (objectsAreReachable ? "reachable" : "unreachable"));
333 out.println(" PLAB size is fixed: " + (plabIsFixed ? "yes" : "no"));
334 out.println("Test expectations:");
335 out.println(" PLAB allocation : " + (expectPLABAllocation ? "expected" : "unexpected"));
336 out.println(" Direct allocation : " + (expectDirectAllocation ? "expected" : "unexpected"));
337 }
338
339 /**
340 * @return
341 * true if we expect PLAB allocation
342 * false if no
343 */
344 public boolean isPromotedByPLAB() {
345 return promotedByPLAB;
346 }
347
348 /**
349 * @return
350 * true if it is test case for unreachable objects
351 * false for live objects
352 */
353 public boolean isDeadObjectCase() {
354 return !objectsAreReachable;
355 }
356
357 /**
358 * Returns amount of memory to fill
359 *
360 * @return amount of memory
361 */
362 public long getMemToFill() {
363 return (long) (edenSize) * 1024l * 1024l / 2;
364 }
365 }
366 }
--- EOF ---