1 /* 2 * Copyright (c) 2015, 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 TestPLABNoResize 26 * @bug 8141278 27 * @summary Test for PLAB allocation in Survivor and Old 28 * @requires vm.gc=="G1" | vm.gc=="null" 29 * @library /testlibrary /../../test/lib / 30 * @modules java.management 31 * @build ClassFileInstaller 32 * sun.hotspot.WhiteBox 33 * gc.g1.plab.lib.Storage 34 * gc.g1.plab.lib.LogParser 35 * gc.g1.plab.lib.AppPLABFixedSize 36 * @run main ClassFileInstaller sun.hotspot.WhiteBox 37 * sun.hotspot.WhiteBox$WhiteBoxPermission 38 * @run main gc.g1.plab.TestPLABNoResize 39 */ 40 package gc.g1.plab; 41 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Arrays; 45 import java.io.PrintStream; 46 47 import gc.g1.plab.lib.AppPLABFixedSize; 48 import gc.g1.plab.lib.LogParser; 49 import gc.g1.plab.lib.PLABUtils; 50 51 import jdk.test.lib.OutputAnalyzer; 52 import jdk.test.lib.ProcessTools; 53 import jdk.test.lib.Platform; 54 55 /** 56 * Test for fixed size PLAB. 57 */ 58 public class TestPLABNoResize { 59 60 // GC ID with survivor PLAB statistics 61 private final static long GC_ID_SURVIVOR_STATS = 1l; 62 // GC ID with old PLAB statistics 63 private final static long GC_ID_OLD_STATS = 2l; 64 65 // Some threshold. We do not expect actual size of promoted with PLAB, 66 // just check that 'dead objects were not promoted','small objects were promoted' and 67 // 'large object were not promoted'. 68 private final static long MEM_CONSUMPTION_THRESHOLD = 256l * 1024l; 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 85 private final static TestCase[] TEST_CASES = { 86 // Test cases for unreachable object 87 new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, false, false), 88 new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, false, false), 89 // Test cases for reachable objects 90 new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, true, true), 91 new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_MEDIUM, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, true, true), 92 new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, true, false), 93 new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, true, true), 94 new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, true, true), 95 new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, true, true), 96 new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_SMALL, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, true, true), 97 new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, true, true), 98 new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_HIGH, true, false) 99 }; 100 101 private static final String[] PLAB_OPTIONS = { 102 "-XX:-ResizePLAB" 103 }; 104 105 public static void main(String[] args) throws Throwable { 106 107 for (TestCase testCase : TEST_CASES) { 108 // What we going to check. 109 testCase.print(System.out); 110 List<String> options = PLABUtils.prepareOptions(PLAB_OPTIONS); 111 options.addAll(testCase.toOptions()); 112 options.add(AppPLABFixedSize.class.getName()); 113 OutputAnalyzer out = ProcessTools.executeTestJvm(options.toArray(new String[options.size()])); 114 if (out.getExitValue() != 0) { 115 System.out.println(out.getOutput()); 116 throw new RuntimeException("Expect exit code 0."); 117 } 118 checkResults(out.getOutput(), testCase); 119 } 120 } 121 122 private static void checkResults(String output, TestCase testCase) { 123 long plabAllocatedSurvivor; 124 long directAllocatedSurvivor; 125 long plabAllocatedOld; 126 long directAllocatedOld; 127 long memAllocated = testCase.getMemToFill(); 128 long wordSize = Platform.is32bit() ? 4l : 8l; 129 LogParser logParser = new LogParser(output); 130 131 Map<String, Long> survivorStats = getPlabStats(logParser, LogParser.ReportType.SURVIVOR_STATS, GC_ID_SURVIVOR_STATS); 132 Map<String, Long> oldStats = getPlabStats(logParser, LogParser.ReportType.OLD_STATS, GC_ID_OLD_STATS); 133 134 plabAllocatedSurvivor = wordSize * survivorStats.get("used"); 135 directAllocatedSurvivor = wordSize * survivorStats.get("direct_allocated"); 136 plabAllocatedOld = wordSize * oldStats.get("used"); 137 directAllocatedOld = wordSize * oldStats.get("direct_allocated"); 138 139 System.out.printf("Survivor PLAB allocated:%17d Direct allocated: %17d Mem consumed:%17d%n", plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated); 140 System.out.printf("Old PLAB allocated:%17d Direct allocated: %17d Mem consumed:%17d%n", plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated); 141 142 // Unreachable object case 143 if (testCase.isDeadObjectCase()) { 144 // All dead object should not be promoted 145 if (plabAllocatedSurvivor > MEM_CONSUMPTION_THRESHOLD || directAllocatedSurvivor > MEM_CONSUMPTION_THRESHOLD) { 146 System.out.println(output); 147 throw new RuntimeException("Unreachable objects should not be allocated using PLAB or direct allocated to Survivor"); 148 } 149 if (plabAllocatedOld > MEM_CONSUMPTION_THRESHOLD || directAllocatedOld > MEM_CONSUMPTION_THRESHOLD) { 150 System.out.println(output); 151 throw new RuntimeException("Unreachable objects should not be allocated using PLAB or direct allocated to Old"); 152 } 153 } else { 154 // All live small objects should be promoted using PLAB 155 if (testCase.isPromotedByPLAB() && Math.abs(plabAllocatedSurvivor - memAllocated) > MEM_CONSUMPTION_THRESHOLD) { 156 System.out.println(output); 157 throw new RuntimeException("Expect that Survivor PLAB allocation are similar to all mem consumed"); 158 } 159 if (testCase.isPromotedByPLAB() && Math.abs(plabAllocatedOld - memAllocated) > MEM_CONSUMPTION_THRESHOLD) { 160 System.out.println(output); 161 throw new RuntimeException("Expect that Old PLAB allocation are similar to all mem consumed"); 162 } 163 164 // All big objects should be directly allocated 165 if (!testCase.isPromotedByPLAB() && Math.abs(directAllocatedSurvivor - memAllocated) > MEM_CONSUMPTION_THRESHOLD) { 166 System.out.println(output); 167 throw new RuntimeException("Test fails. Expect that Survivor direct allocation are similar to all mem consumed"); 168 } 169 if (!testCase.isPromotedByPLAB() && Math.abs(directAllocatedOld - memAllocated) > MEM_CONSUMPTION_THRESHOLD) { 170 System.out.println(output); 171 throw new RuntimeException("Test fails. Expect that Old direct allocation are similar to all mem consumed"); 172 } 173 } 174 System.out.println("Test passed!"); 175 } 176 177 private static Map<String, Long> getPlabStats(LogParser logParser, LogParser.ReportType type, long gc_id) { 178 Map<String, Long> survivorStats = logParser.getEntries() 179 .stream() 180 .filter(p -> p.first == type) 181 .map(p -> p.second) 182 .filter(l -> l.get(LogParser.GC_ID) == gc_id) 183 .findFirst() 184 .orElseThrow(() -> { 185 System.out.println(logParser.getLog()); 186 return new RuntimeException("Cannot find requested GC_ID - " + gc_id); 187 }); 188 return survivorStats; 189 } 190 191 /** 192 * Description of one test case. 193 */ 194 private static class TestCase { 195 196 private final int wastePct; 197 private final int plabSize; 198 private final int chunkSize; 199 private final int parGCThreads; 200 private final int edenSize; 201 private final boolean objectsAreReachable; 202 private final boolean promotedByPLAB; 203 204 /** 205 * @param wastePct 206 * ParallelGCBufferWastePct 207 * @param plabSize 208 * -XX:OldPLABSize and -XX:YoungPLABSize 209 * @param chunkSize 210 * requested object size for memory consumption 211 * @param parGCThreads 212 * -XX:ParallelGCThreads 213 * @param objectsAreReachable 214 * true - allocate live objects 215 * false - allocate unreachable objects 216 * @param promotedByPLAB 217 * true - we expect to see PLAB allocation during promotion 218 * false - objects will be directly allocated during promotion 219 */ 220 public TestCase(int wastePct, 221 int plabSize, 222 int chunkSize, 223 int parGCThreads, 224 int edenSize, 225 boolean objectsAreReachable, 226 boolean promotedByPLAB 227 ) { 228 if (wastePct == 0 || plabSize == 0 || chunkSize == 0 || parGCThreads == 0 || edenSize == 0) { 229 throw new IllegalArgumentException("Parameters should not be 0"); 230 } 231 this.wastePct = wastePct; 232 this.plabSize = plabSize; 233 this.chunkSize = chunkSize; 234 this.parGCThreads = parGCThreads; 235 this.edenSize = edenSize; 236 this.objectsAreReachable = objectsAreReachable; 237 this.promotedByPLAB = promotedByPLAB; 238 } 239 240 /** 241 * Convert current TestCase to List of options. 242 * Assume test will fill half of existed eden. 243 * 244 * @return 245 * List of options 246 */ 247 public List<String> toOptions() { 248 return Arrays.asList("-XX:ParallelGCThreads=" + parGCThreads, 249 "-XX:ParallelGCBufferWastePct=" + wastePct, 250 "-XX:OldPLABSize=" + plabSize, 251 "-XX:YoungPLABSize=" + plabSize, 252 "-Dchunk.size=" + chunkSize, 253 "-Dreachable=" + objectsAreReachable, 254 "-XX:NewSize=" + edenSize + "m", 255 "-XX:MaxNewSize=" + edenSize + "m", 256 "-Dmem.to.fill=" + getMemToFill() 257 ); 258 } 259 260 /** 261 * Print details about test case. 262 */ 263 public void print(PrintStream out) { 264 boolean expectPLABAllocation = promotedByPLAB && objectsAreReachable; 265 boolean expectDirectAllocation = (!promotedByPLAB) && objectsAreReachable; 266 267 out.println("Test case details:"); 268 out.println(" Young gen size : " + edenSize + "M"); 269 out.println(" Predefined PLAB size : " + plabSize); 270 out.println(" Parallel GC buffer waste pct : " + wastePct); 271 out.println(" Chunk size : " + chunkSize); 272 out.println(" Parallel GC threads : " + parGCThreads); 273 out.println(" Objects are created : " + (objectsAreReachable ? "reachable" : "unreachable")); 274 out.println("Test expectations:"); 275 out.println(" PLAB allocation : " + (expectPLABAllocation ? "expected" : "unexpected")); 276 out.println(" Direct allocation : " + (expectDirectAllocation ? "expected" : "unexpected")); 277 } 278 279 /** 280 * @return 281 * true if we expect PLAB allocation 282 * false if no 283 */ 284 public boolean isPromotedByPLAB() { 285 return promotedByPLAB; 286 } 287 288 /** 289 * @return 290 * true if it is test case for unreachable objects 291 * false - for live objects 292 */ 293 public boolean isDeadObjectCase() { 294 return !objectsAreReachable; 295 } 296 297 /** 298 * Returns amount of memory to fill 299 * 300 * @return amount of memory 301 */ 302 public long getMemToFill() { 303 return (long) (edenSize) * 1024l * 1024l / 2; 304 } 305 } 306 }