1 /* 2 * Copyright (c) 2016, 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 * sun.hotspot.WhiteBox$WhiteBoxPermission 36 * @run main/timeout=240 gc.g1.plab.TestPLABPromotion 37 */ 38 package gc.g1.plab; 39 40 import java.util.List; 41 import java.util.Arrays; 42 import java.io.PrintStream; 43 44 import gc.g1.plab.lib.AppPLABPromotion; 45 import gc.g1.plab.lib.LogParser; 46 import gc.g1.plab.lib.PLABUtils; 47 import gc.g1.plab.lib.PlabInfo; 48 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 = 1000; 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 main(String[] args)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.toArray(new String[options.size()])); 121 PLABUtils.commonCheck(out); 122 output = out.getOutput(); 123 checkResults(testCase); 124 } 125 } 126 checkResults(TestCase testCase)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 checkTotalPromotion(long plabAllocatedSurvivor, long directAllocatedSurvivor, long memAllocated, String exceptionMessage)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 */ checkLiveObjectsPromotion(long plabAllocated, long totalMemAllocated, String exceptionMessage)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 */ checkDeadObjectsPromotion(long plabPromoted, long directlyPromoted, long memoryAllocated)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 */ checkFields(PlabInfo info)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 */ checkRatio(long checkedValue, long controlValue)225 private static boolean checkRatio(long checkedValue, long controlValue) { 226 return (Math.abs(checkedValue) / controlValue) * 100L < 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 */ checkDifferenceRatio(long checkedValue, long controlValue)238 private static boolean checkDifferenceRatio(long checkedValue, long controlValue) { 239 return (Math.abs(checkedValue - controlValue) / controlValue) * 100L < 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 */ TestCase(int wastePct, int plabSize, int chunkSize, int parGCThreads, int edenSize, boolean plabIsFixed, boolean objectsAreReachable, boolean promotedByPLAB )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 */ toOptions()305 public List<String> toOptions() { 306 return Arrays.asList("-XX:ParallelGCThreads=" + parGCThreads, 307 "-XX:ParallelGCBufferWastePct=" + wastePct, 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 */ print(PrintStream out)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 */ isPromotedByPLAB()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 */ isDeadObjectCase()353 public boolean isDeadObjectCase() { 354 return !objectsAreReachable; 355 } 356 357 /** 358 * Returns amount of memory to fill 359 * 360 * @return amount of memory 361 */ getMemToFill()362 public long getMemToFill() { 363 return (long) (edenSize) * 1024l * 1024l / 2; 364 } 365 } 366 } 367