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