test/gc/g1/TestStringDeduplicationTools.java

Mon, 06 Nov 2017 16:51:47 +0800

author
aoqi
date
Mon, 06 Nov 2017 16:51:47 +0800
changeset 7997
6cbff0651f1a
parent 6876
710a3c8b516e
permissions
-rw-r--r--

[Code Reorganization] remove trailing whitespace to pass jcheck test

aoqi@0 1 /*
aoqi@0 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation.
aoqi@0 8 *
aoqi@0 9 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 12 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 13 * accompanied this code).
aoqi@0 14 *
aoqi@0 15 * You should have received a copy of the GNU General Public License version
aoqi@0 16 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 18 *
aoqi@0 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 20 * or visit www.oracle.com if you need additional information or have any
aoqi@0 21 * questions.
aoqi@0 22 */
aoqi@0 23
aoqi@0 24 /*
aoqi@0 25 * Common code for string deduplication tests
aoqi@0 26 */
aoqi@0 27
aoqi@0 28 import java.lang.management.*;
aoqi@0 29 import java.lang.reflect.*;
aoqi@0 30 import java.security.*;
aoqi@0 31 import java.util.*;
aoqi@0 32 import com.oracle.java.testlibrary.*;
aoqi@0 33 import sun.misc.*;
aoqi@0 34
aoqi@0 35 class TestStringDeduplicationTools {
aoqi@0 36 private static final String YoungGC = "YoungGC";
aoqi@0 37 private static final String FullGC = "FullGC";
aoqi@0 38
aoqi@0 39 private static final int Xmn = 50; // MB
aoqi@0 40 private static final int Xms = 100; // MB
aoqi@0 41 private static final int Xmx = 100; // MB
aoqi@0 42 private static final int MB = 1024 * 1024;
aoqi@0 43 private static final int StringLength = 50;
aoqi@0 44
aoqi@0 45 private static Field valueField;
aoqi@0 46 private static Unsafe unsafe;
aoqi@0 47 private static byte[] dummy;
aoqi@0 48
aoqi@0 49 static {
aoqi@0 50 try {
aoqi@0 51 Field field = Unsafe.class.getDeclaredField("theUnsafe");
aoqi@0 52 field.setAccessible(true);
aoqi@0 53 unsafe = (Unsafe)field.get(null);
aoqi@0 54
aoqi@0 55 valueField = String.class.getDeclaredField("value");
aoqi@0 56 valueField.setAccessible(true);
aoqi@0 57 } catch (Exception e) {
aoqi@0 58 throw new RuntimeException(e);
aoqi@0 59 }
aoqi@0 60 }
aoqi@0 61
aoqi@0 62 private static Object getValue(String string) {
aoqi@0 63 try {
aoqi@0 64 return valueField.get(string);
aoqi@0 65 } catch (Exception e) {
aoqi@0 66 throw new RuntimeException(e);
aoqi@0 67 }
aoqi@0 68 }
aoqi@0 69
aoqi@0 70 private static void doFullGc(int numberOfTimes) {
aoqi@0 71 for (int i = 0; i < numberOfTimes; i++) {
aoqi@0 72 System.out.println("Begin: Full GC " + (i + 1) + "/" + numberOfTimes);
aoqi@0 73 System.gc();
aoqi@0 74 System.out.println("End: Full GC " + (i + 1) + "/" + numberOfTimes);
aoqi@0 75 }
aoqi@0 76 }
aoqi@0 77
aoqi@0 78 private static void doYoungGc(int numberOfTimes) {
aoqi@0 79 // Provoke at least numberOfTimes young GCs
aoqi@0 80 final int objectSize = 128;
aoqi@0 81 final int maxObjectInYoung = (Xmn * MB) / objectSize;
aoqi@0 82 for (int i = 0; i < numberOfTimes; i++) {
aoqi@0 83 System.out.println("Begin: Young GC " + (i + 1) + "/" + numberOfTimes);
aoqi@0 84 for (int j = 0; j < maxObjectInYoung + 1; j++) {
aoqi@0 85 dummy = new byte[objectSize];
aoqi@0 86 }
aoqi@0 87 System.out.println("End: Young GC " + (i + 1) + "/" + numberOfTimes);
aoqi@0 88 }
aoqi@0 89 }
aoqi@0 90
aoqi@0 91 private static void forceDeduplication(int ageThreshold, String gcType) {
aoqi@0 92 // Force deduplication to happen by either causing a FullGC or a YoungGC.
aoqi@0 93 // We do several collections to also provoke a situation where the the
aoqi@0 94 // deduplication thread needs to yield while processing the queue. This
aoqi@0 95 // also tests that the references in the deduplication queue are adjusted
aoqi@0 96 // accordingly.
aoqi@0 97 if (gcType.equals(FullGC)) {
aoqi@0 98 doFullGc(3);
aoqi@0 99 } else {
aoqi@0 100 doYoungGc(ageThreshold + 3);
aoqi@0 101 }
aoqi@0 102 }
aoqi@0 103
aoqi@0 104 private static String generateString(int id) {
aoqi@0 105 StringBuilder builder = new StringBuilder(StringLength);
aoqi@0 106
aoqi@0 107 builder.append("DeduplicationTestString:" + id + ":");
aoqi@0 108
aoqi@0 109 while (builder.length() < StringLength) {
aoqi@0 110 builder.append('X');
aoqi@0 111 }
aoqi@0 112
aoqi@0 113 return builder.toString();
aoqi@0 114 }
aoqi@0 115
aoqi@0 116 private static ArrayList<String> createStrings(int total, int unique) {
aoqi@0 117 System.out.println("Creating strings: total=" + total + ", unique=" + unique);
aoqi@0 118 if (total % unique != 0) {
aoqi@0 119 throw new RuntimeException("Total must be divisible by unique");
aoqi@0 120 }
aoqi@0 121
aoqi@0 122 ArrayList<String> list = new ArrayList<String>(total);
aoqi@0 123 for (int j = 0; j < total / unique; j++) {
aoqi@0 124 for (int i = 0; i < unique; i++) {
aoqi@0 125 list.add(generateString(i));
aoqi@0 126 }
aoqi@0 127 }
aoqi@0 128
aoqi@0 129 return list;
aoqi@0 130 }
aoqi@0 131
aoqi@0 132 private static void verifyStrings(ArrayList<String> list, int uniqueExpected) {
aoqi@0 133 for (;;) {
aoqi@0 134 // Check number of deduplicated strings
aoqi@0 135 ArrayList<Object> unique = new ArrayList<Object>(uniqueExpected);
aoqi@0 136 for (String string: list) {
aoqi@0 137 Object value = getValue(string);
aoqi@0 138 boolean uniqueValue = true;
aoqi@0 139 for (Object obj: unique) {
aoqi@0 140 if (obj == value) {
aoqi@0 141 uniqueValue = false;
aoqi@0 142 break;
aoqi@0 143 }
aoqi@0 144 }
aoqi@0 145
aoqi@0 146 if (uniqueValue) {
aoqi@0 147 unique.add(value);
aoqi@0 148 }
aoqi@0 149 }
aoqi@0 150
aoqi@0 151 System.out.println("Verifying strings: total=" + list.size() +
aoqi@0 152 ", uniqueFound=" + unique.size() +
aoqi@0 153 ", uniqueExpected=" + uniqueExpected);
aoqi@0 154
aoqi@0 155 if (unique.size() == uniqueExpected) {
aoqi@0 156 System.out.println("Deduplication completed");
aoqi@0 157 break;
aoqi@0 158 } else {
aoqi@0 159 System.out.println("Deduplication not completed, waiting...");
aoqi@0 160
aoqi@0 161 // Give the deduplication thread time to complete
aoqi@0 162 try {
aoqi@0 163 Thread.sleep(1000);
aoqi@0 164 } catch (Exception e) {
aoqi@0 165 throw new RuntimeException(e);
aoqi@0 166 }
aoqi@0 167 }
aoqi@0 168 }
aoqi@0 169 }
aoqi@0 170
aoqi@0 171 private static OutputAnalyzer runTest(String... extraArgs) throws Exception {
aoqi@0 172 String[] defaultArgs = new String[] {
aoqi@0 173 "-Xmn" + Xmn + "m",
aoqi@0 174 "-Xms" + Xms + "m",
aoqi@0 175 "-Xmx" + Xmx + "m",
aoqi@0 176 "-XX:+UseG1GC",
aoqi@0 177 "-XX:+UnlockDiagnosticVMOptions",
aoqi@0 178 "-XX:+VerifyAfterGC" // Always verify after GC
aoqi@0 179 };
aoqi@0 180
aoqi@0 181 ArrayList<String> args = new ArrayList<String>();
aoqi@0 182 args.addAll(Arrays.asList(defaultArgs));
aoqi@0 183 args.addAll(Arrays.asList(extraArgs));
aoqi@0 184
aoqi@0 185 ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args.toArray(new String[args.size()]));
aoqi@0 186 OutputAnalyzer output = new OutputAnalyzer(pb.start());
aoqi@0 187 System.err.println(output.getStderr());
aoqi@0 188 System.out.println(output.getStdout());
aoqi@0 189 return output;
aoqi@0 190 }
aoqi@0 191
aoqi@0 192 private static class DeduplicationTest {
aoqi@0 193 public static void main(String[] args) {
aoqi@0 194 System.out.println("Begin: DeduplicationTest");
aoqi@0 195
aoqi@0 196 final int numberOfStrings = Integer.parseUnsignedInt(args[0]);
aoqi@0 197 final int numberOfUniqueStrings = Integer.parseUnsignedInt(args[1]);
aoqi@0 198 final int ageThreshold = Integer.parseUnsignedInt(args[2]);
aoqi@0 199 final String gcType = args[3];
aoqi@0 200
aoqi@0 201 ArrayList<String> list = createStrings(numberOfStrings, numberOfUniqueStrings);
aoqi@0 202 forceDeduplication(ageThreshold, gcType);
aoqi@0 203 verifyStrings(list, numberOfUniqueStrings);
aoqi@0 204
aoqi@0 205 System.out.println("End: DeduplicationTest");
aoqi@0 206 }
aoqi@0 207
aoqi@0 208 public static OutputAnalyzer run(int numberOfStrings, int ageThreshold, String gcType, String... extraArgs) throws Exception {
aoqi@0 209 String[] defaultArgs = new String[] {
aoqi@0 210 "-XX:+UseStringDeduplication",
aoqi@0 211 "-XX:StringDeduplicationAgeThreshold=" + ageThreshold,
aoqi@0 212 DeduplicationTest.class.getName(),
aoqi@0 213 "" + numberOfStrings,
aoqi@0 214 "" + numberOfStrings / 2,
aoqi@0 215 "" + ageThreshold,
aoqi@0 216 gcType
aoqi@0 217 };
aoqi@0 218
aoqi@0 219 ArrayList<String> args = new ArrayList<String>();
aoqi@0 220 args.addAll(Arrays.asList(extraArgs));
aoqi@0 221 args.addAll(Arrays.asList(defaultArgs));
aoqi@0 222
aoqi@0 223 return runTest(args.toArray(new String[args.size()]));
aoqi@0 224 }
aoqi@0 225 }
aoqi@0 226
aoqi@0 227 private static class InternedTest {
aoqi@0 228 public static void main(String[] args) {
aoqi@0 229 // This test verifies that interned strings are always
aoqi@0 230 // deduplicated when being interned, and never after
aoqi@0 231 // being interned.
aoqi@0 232
aoqi@0 233 System.out.println("Begin: InternedTest");
aoqi@0 234
aoqi@0 235 final int ageThreshold = Integer.parseUnsignedInt(args[0]);
aoqi@0 236 final String baseString = "DeduplicationTestString:" + InternedTest.class.getName();
aoqi@0 237
aoqi@0 238 // Create duplicate of baseString
aoqi@0 239 StringBuilder sb1 = new StringBuilder(baseString);
aoqi@0 240 String dupString1 = sb1.toString();
aoqi@0 241 if (getValue(dupString1) == getValue(baseString)) {
aoqi@0 242 throw new RuntimeException("Values should not match");
aoqi@0 243 }
aoqi@0 244
aoqi@0 245 // Force baseString to be inspected for deduplication
aoqi@0 246 // and be inserted into the deduplication hashtable.
aoqi@0 247 forceDeduplication(ageThreshold, FullGC);
aoqi@0 248
aoqi@0 249 // Wait for deduplication to occur
aoqi@0 250 while (getValue(dupString1) != getValue(baseString)) {
aoqi@0 251 System.out.println("Waiting...");
aoqi@0 252 try {
aoqi@0 253 Thread.sleep(100);
aoqi@0 254 } catch (Exception e) {
aoqi@0 255 throw new RuntimeException(e);
aoqi@0 256 }
aoqi@0 257 }
aoqi@0 258
aoqi@0 259 // Create a new duplicate of baseString
aoqi@0 260 StringBuilder sb2 = new StringBuilder(baseString);
aoqi@0 261 String dupString2 = sb2.toString();
aoqi@0 262 if (getValue(dupString2) == getValue(baseString)) {
aoqi@0 263 throw new RuntimeException("Values should not match");
aoqi@0 264 }
aoqi@0 265
aoqi@0 266 // Intern the new duplicate
aoqi@0 267 Object beforeInternedValue = getValue(dupString2);
aoqi@0 268 String internedString = dupString2.intern();
aoqi@0 269 if (internedString != dupString2) {
aoqi@0 270 throw new RuntimeException("String should match");
aoqi@0 271 }
aoqi@0 272 if (getValue(internedString) != getValue(baseString)) {
aoqi@0 273 throw new RuntimeException("Values should match");
aoqi@0 274 }
aoqi@0 275
aoqi@0 276 // Check original value of interned string, to make sure
aoqi@0 277 // deduplication happened on the interned string and not
aoqi@0 278 // on the base string
aoqi@0 279 if (beforeInternedValue == getValue(baseString)) {
aoqi@0 280 throw new RuntimeException("Values should not match");
aoqi@0 281 }
aoqi@0 282
aoqi@0 283 System.out.println("End: InternedTest");
aoqi@0 284 }
aoqi@0 285
aoqi@0 286 public static OutputAnalyzer run() throws Exception {
aoqi@0 287 return runTest("-XX:+PrintGC",
aoqi@0 288 "-XX:+PrintGCDetails",
aoqi@0 289 "-XX:+UseStringDeduplication",
aoqi@0 290 "-XX:+PrintStringDeduplicationStatistics",
aoqi@0 291 "-XX:StringDeduplicationAgeThreshold=" + DefaultAgeThreshold,
aoqi@0 292 InternedTest.class.getName(),
aoqi@0 293 "" + DefaultAgeThreshold);
aoqi@0 294 }
aoqi@0 295 }
aoqi@0 296
aoqi@0 297 /*
aoqi@0 298 * Tests
aoqi@0 299 */
aoqi@0 300
aoqi@0 301 private static final int LargeNumberOfStrings = 10000;
aoqi@0 302 private static final int SmallNumberOfStrings = 10;
aoqi@0 303
aoqi@0 304 private static final int MaxAgeThreshold = 15;
aoqi@0 305 private static final int DefaultAgeThreshold = 3;
aoqi@0 306 private static final int MinAgeThreshold = 1;
aoqi@0 307
aoqi@0 308 private static final int TooLowAgeThreshold = MinAgeThreshold - 1;
aoqi@0 309 private static final int TooHighAgeThreshold = MaxAgeThreshold + 1;
aoqi@0 310
aoqi@0 311 public static void testYoungGC() throws Exception {
aoqi@0 312 // Do young GC to age strings to provoke deduplication
aoqi@0 313 OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,
aoqi@0 314 DefaultAgeThreshold,
aoqi@0 315 YoungGC,
aoqi@0 316 "-XX:+PrintGC",
aoqi@0 317 "-XX:+PrintStringDeduplicationStatistics");
aoqi@0 318 output.shouldNotContain("Full GC");
aoqi@0 319 output.shouldContain("GC pause (G1 Evacuation Pause) (young)");
aoqi@0 320 output.shouldContain("GC concurrent-string-deduplication");
aoqi@0 321 output.shouldContain("Deduplicated:");
aoqi@0 322 output.shouldHaveExitValue(0);
aoqi@0 323 }
aoqi@0 324
aoqi@0 325 public static void testFullGC() throws Exception {
aoqi@0 326 // Do full GC to age strings to provoke deduplication
aoqi@0 327 OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,
aoqi@0 328 DefaultAgeThreshold,
aoqi@0 329 FullGC,
aoqi@0 330 "-XX:+PrintGC",
aoqi@0 331 "-XX:+PrintStringDeduplicationStatistics");
aoqi@0 332 output.shouldNotContain("GC pause (G1 Evacuation Pause) (young)");
aoqi@0 333 output.shouldContain("Full GC");
aoqi@0 334 output.shouldContain("GC concurrent-string-deduplication");
aoqi@0 335 output.shouldContain("Deduplicated:");
aoqi@0 336 output.shouldHaveExitValue(0);
aoqi@0 337 }
aoqi@0 338
aoqi@0 339 public static void testTableResize() throws Exception {
aoqi@0 340 // Test with StringDeduplicationResizeALot
aoqi@0 341 OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,
aoqi@0 342 DefaultAgeThreshold,
aoqi@0 343 YoungGC,
aoqi@0 344 "-XX:+PrintGC",
aoqi@0 345 "-XX:+PrintStringDeduplicationStatistics",
aoqi@0 346 "-XX:+StringDeduplicationResizeALot");
aoqi@0 347 output.shouldContain("GC concurrent-string-deduplication");
aoqi@0 348 output.shouldContain("Deduplicated:");
aoqi@0 349 output.shouldNotContain("Resize Count: 0");
aoqi@0 350 output.shouldHaveExitValue(0);
aoqi@0 351 }
aoqi@0 352
aoqi@0 353 public static void testTableRehash() throws Exception {
aoqi@0 354 // Test with StringDeduplicationRehashALot
aoqi@0 355 OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,
aoqi@0 356 DefaultAgeThreshold,
aoqi@0 357 YoungGC,
aoqi@0 358 "-XX:+PrintGC",
aoqi@0 359 "-XX:+PrintStringDeduplicationStatistics",
aoqi@0 360 "-XX:+StringDeduplicationRehashALot");
aoqi@0 361 output.shouldContain("GC concurrent-string-deduplication");
aoqi@0 362 output.shouldContain("Deduplicated:");
aoqi@0 363 output.shouldNotContain("Rehash Count: 0");
aoqi@0 364 output.shouldNotContain("Hash Seed: 0x0");
aoqi@0 365 output.shouldHaveExitValue(0);
aoqi@0 366 }
aoqi@0 367
aoqi@0 368 public static void testAgeThreshold() throws Exception {
aoqi@0 369 OutputAnalyzer output;
aoqi@0 370
aoqi@0 371 // Test with max age theshold
aoqi@0 372 output = DeduplicationTest.run(SmallNumberOfStrings,
aoqi@0 373 MaxAgeThreshold,
aoqi@0 374 YoungGC,
aoqi@0 375 "-XX:+PrintGC",
aoqi@0 376 "-XX:+PrintStringDeduplicationStatistics");
aoqi@0 377 output.shouldContain("GC concurrent-string-deduplication");
aoqi@0 378 output.shouldContain("Deduplicated:");
aoqi@0 379 output.shouldHaveExitValue(0);
aoqi@0 380
aoqi@0 381 // Test with min age theshold
aoqi@0 382 output = DeduplicationTest.run(SmallNumberOfStrings,
aoqi@0 383 MinAgeThreshold,
aoqi@0 384 YoungGC,
aoqi@0 385 "-XX:+PrintGC",
aoqi@0 386 "-XX:+PrintStringDeduplicationStatistics");
aoqi@0 387 output.shouldContain("GC concurrent-string-deduplication");
aoqi@0 388 output.shouldContain("Deduplicated:");
aoqi@0 389 output.shouldHaveExitValue(0);
aoqi@0 390
aoqi@0 391 // Test with too low age threshold
aoqi@0 392 output = DeduplicationTest.run(SmallNumberOfStrings,
aoqi@0 393 TooLowAgeThreshold,
aoqi@0 394 YoungGC);
aoqi@0 395 output.shouldContain("StringDeduplicationAgeThreshold of " + TooLowAgeThreshold +
aoqi@0 396 " is invalid; must be between " + MinAgeThreshold + " and " + MaxAgeThreshold);
aoqi@0 397 output.shouldHaveExitValue(1);
aoqi@0 398
aoqi@0 399 // Test with too high age threshold
aoqi@0 400 output = DeduplicationTest.run(SmallNumberOfStrings,
aoqi@0 401 TooHighAgeThreshold,
aoqi@0 402 YoungGC);
aoqi@0 403 output.shouldContain("StringDeduplicationAgeThreshold of " + TooHighAgeThreshold +
aoqi@0 404 " is invalid; must be between " + MinAgeThreshold + " and " + MaxAgeThreshold);
aoqi@0 405 output.shouldHaveExitValue(1);
aoqi@0 406 }
aoqi@0 407
aoqi@0 408 public static void testPrintOptions() throws Exception {
aoqi@0 409 OutputAnalyzer output;
aoqi@0 410
aoqi@0 411 // Test without PrintGC and without PrintStringDeduplicationStatistics
aoqi@0 412 output = DeduplicationTest.run(SmallNumberOfStrings,
aoqi@0 413 DefaultAgeThreshold,
aoqi@0 414 YoungGC);
aoqi@0 415 output.shouldNotContain("GC concurrent-string-deduplication");
aoqi@0 416 output.shouldNotContain("Deduplicated:");
aoqi@0 417 output.shouldHaveExitValue(0);
aoqi@0 418
aoqi@0 419 // Test with PrintGC but without PrintStringDeduplicationStatistics
aoqi@0 420 output = DeduplicationTest.run(SmallNumberOfStrings,
aoqi@0 421 DefaultAgeThreshold,
aoqi@0 422 YoungGC,
aoqi@0 423 "-XX:+PrintGC");
aoqi@0 424 output.shouldContain("GC concurrent-string-deduplication");
aoqi@0 425 output.shouldNotContain("Deduplicated:");
aoqi@0 426 output.shouldHaveExitValue(0);
aoqi@0 427 }
aoqi@0 428
aoqi@0 429 public static void testInterned() throws Exception {
aoqi@0 430 // Test that interned strings are deduplicated before being interned
aoqi@0 431 OutputAnalyzer output = InternedTest.run();
aoqi@0 432 output.shouldHaveExitValue(0);
aoqi@0 433 }
aoqi@0 434 }

mercurial