1 /* 2 * Copyright (c) 2016, 2020, 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 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 = 1000; 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 main(String[] args)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 checkResults(TestCase testCase)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 checkTotalPromotion(long plabAllocatedSurvivor, long directAllocatedSurvivor, long memAllocated, String exceptionMessage)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 */ checkLiveObjectsPromotion(long plabAllocated, long totalMemAllocated, String exceptionMessage)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 */ checkDeadObjectsPromotion(long plabPromoted, long directlyPromoted, long memoryAllocated)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 */ checkFields(PlabInfo info)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 */ checkRatio(long checkedValue, long controlValue)224 private static boolean checkRatio(long checkedValue, long controlValue) { 225 return (Math.abs(checkedValue) / controlValue) * 100L < 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 */ checkDifferenceRatio(long checkedValue, long controlValue)237 private static boolean checkDifferenceRatio(long checkedValue, long controlValue) { 238 return (Math.abs(checkedValue - controlValue) / controlValue) * 100L < 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 */ TestCase(int wastePct, int plabSize, int chunkSize, int parGCThreads, int edenSize, boolean plabIsFixed, boolean objectsAreReachable, boolean promotedByPLAB )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 */ toOptions()304 public List<String> toOptions() { 305 return Arrays.asList("-XX:ParallelGCThreads=" + parGCThreads, 306 "-XX:ParallelGCBufferWastePct=" + wastePct, 307 "-XX:OldPLABSize=" + plabSize, 308 "-XX:YoungPLABSize=" + plabSize, 309 "-XX:" + (plabIsFixed ? "-" : "+") + "ResizePLAB", 310 "-Dchunk.size=" + chunkSize, 311 "-Dreachable=" + objectsAreReachable, 312 "-XX:NewSize=" + edenSize + "m", 313 "-XX:MaxNewSize=" + edenSize + "m", 314 "-Dmem.to.fill=" + getMemToFill() 315 ); 316 } 317 318 /** 319 * Print details about test case. 320 */ print(PrintStream out)321 public void print(PrintStream out) { 322 boolean expectPLABAllocation = promotedByPLAB && objectsAreReachable; 323 boolean expectDirectAllocation = (!promotedByPLAB) && objectsAreReachable; 324 325 out.println("Test case details:"); 326 out.println(" Young gen size : " + edenSize + "M"); 327 out.println(" Predefined PLAB size : " + plabSize); 328 out.println(" Parallel GC buffer waste pct : " + wastePct); 329 out.println(" Chunk size : " + chunkSize); 330 out.println(" Parallel GC threads : " + parGCThreads); 331 out.println(" Objects are created : " + (objectsAreReachable ? "reachable" : "unreachable")); 332 out.println(" PLAB size is fixed: " + (plabIsFixed ? "yes" : "no")); 333 out.println("Test expectations:"); 334 out.println(" PLAB allocation : " + (expectPLABAllocation ? "expected" : "unexpected")); 335 out.println(" Direct allocation : " + (expectDirectAllocation ? "expected" : "unexpected")); 336 } 337 338 /** 339 * @return 340 * true if we expect PLAB allocation 341 * false if no 342 */ isPromotedByPLAB()343 public boolean isPromotedByPLAB() { 344 return promotedByPLAB; 345 } 346 347 /** 348 * @return 349 * true if it is test case for unreachable objects 350 * false for live objects 351 */ isDeadObjectCase()352 public boolean isDeadObjectCase() { 353 return !objectsAreReachable; 354 } 355 356 /** 357 * Returns amount of memory to fill 358 * 359 * @return amount of memory 360 */ getMemToFill()361 public long getMemToFill() { 362 return (long) (edenSize) * 1024l * 1024l / 2; 363 } 364 } 365 } 366