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