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