Path: blob/aarch64-shenandoah-jdk8u272-b10/hotspot/test/gc/g1/TestStringDeduplicationTools.java
32284 views
/*1* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* Common code for string deduplication tests25*/2627import java.lang.management.*;28import java.lang.reflect.*;29import java.security.*;30import java.util.*;31import com.oracle.java.testlibrary.*;32import sun.misc.*;3334class TestStringDeduplicationTools {35private static final String YoungGC = "YoungGC";36private static final String FullGC = "FullGC";3738private static final int Xmn = 50; // MB39private static final int Xms = 100; // MB40private static final int Xmx = 100; // MB41private static final int MB = 1024 * 1024;42private static final int StringLength = 50;4344private static Field valueField;45private static Unsafe unsafe;46private static byte[] dummy;4748static {49try {50Field field = Unsafe.class.getDeclaredField("theUnsafe");51field.setAccessible(true);52unsafe = (Unsafe)field.get(null);5354valueField = String.class.getDeclaredField("value");55valueField.setAccessible(true);56} catch (Exception e) {57throw new RuntimeException(e);58}59}6061private static Object getValue(String string) {62try {63return valueField.get(string);64} catch (Exception e) {65throw new RuntimeException(e);66}67}6869private static void doFullGc(int numberOfTimes) {70for (int i = 0; i < numberOfTimes; i++) {71System.out.println("Begin: Full GC " + (i + 1) + "/" + numberOfTimes);72System.gc();73System.out.println("End: Full GC " + (i + 1) + "/" + numberOfTimes);74}75}7677private static void doYoungGc(int numberOfTimes) {78// Provoke at least numberOfTimes young GCs79final int objectSize = 128;80final int maxObjectInYoung = (Xmn * MB) / objectSize;81for (int i = 0; i < numberOfTimes; i++) {82System.out.println("Begin: Young GC " + (i + 1) + "/" + numberOfTimes);83for (int j = 0; j < maxObjectInYoung + 1; j++) {84dummy = new byte[objectSize];85}86System.out.println("End: Young GC " + (i + 1) + "/" + numberOfTimes);87}88}8990private static void forceDeduplication(int ageThreshold, String gcType) {91// Force deduplication to happen by either causing a FullGC or a YoungGC.92// We do several collections to also provoke a situation where the the93// deduplication thread needs to yield while processing the queue. This94// also tests that the references in the deduplication queue are adjusted95// accordingly.96if (gcType.equals(FullGC)) {97doFullGc(3);98} else {99doYoungGc(ageThreshold + 3);100}101}102103private static String generateString(int id) {104StringBuilder builder = new StringBuilder(StringLength);105106builder.append("DeduplicationTestString:" + id + ":");107108while (builder.length() < StringLength) {109builder.append('X');110}111112return builder.toString();113}114115private static ArrayList<String> createStrings(int total, int unique) {116System.out.println("Creating strings: total=" + total + ", unique=" + unique);117if (total % unique != 0) {118throw new RuntimeException("Total must be divisible by unique");119}120121ArrayList<String> list = new ArrayList<String>(total);122for (int j = 0; j < total / unique; j++) {123for (int i = 0; i < unique; i++) {124list.add(generateString(i));125}126}127128return list;129}130131private static void verifyStrings(ArrayList<String> list, int uniqueExpected) {132for (;;) {133// Check number of deduplicated strings134ArrayList<Object> unique = new ArrayList<Object>(uniqueExpected);135for (String string: list) {136Object value = getValue(string);137boolean uniqueValue = true;138for (Object obj: unique) {139if (obj == value) {140uniqueValue = false;141break;142}143}144145if (uniqueValue) {146unique.add(value);147}148}149150System.out.println("Verifying strings: total=" + list.size() +151", uniqueFound=" + unique.size() +152", uniqueExpected=" + uniqueExpected);153154if (unique.size() == uniqueExpected) {155System.out.println("Deduplication completed");156break;157} else {158System.out.println("Deduplication not completed, waiting...");159160// Give the deduplication thread time to complete161try {162Thread.sleep(1000);163} catch (Exception e) {164throw new RuntimeException(e);165}166}167}168}169170private static OutputAnalyzer runTest(String... extraArgs) throws Exception {171String[] defaultArgs = new String[] {172"-Xmn" + Xmn + "m",173"-Xms" + Xms + "m",174"-Xmx" + Xmx + "m",175"-XX:+UseG1GC",176"-XX:+UnlockDiagnosticVMOptions",177"-XX:+VerifyAfterGC" // Always verify after GC178};179180ArrayList<String> args = new ArrayList<String>();181args.addAll(Arrays.asList(defaultArgs));182args.addAll(Arrays.asList(extraArgs));183184ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args.toArray(new String[args.size()]));185OutputAnalyzer output = new OutputAnalyzer(pb.start());186System.err.println(output.getStderr());187System.out.println(output.getStdout());188return output;189}190191private static class DeduplicationTest {192public static void main(String[] args) {193System.out.println("Begin: DeduplicationTest");194195final int numberOfStrings = Integer.parseUnsignedInt(args[0]);196final int numberOfUniqueStrings = Integer.parseUnsignedInt(args[1]);197final int ageThreshold = Integer.parseUnsignedInt(args[2]);198final String gcType = args[3];199200ArrayList<String> list = createStrings(numberOfStrings, numberOfUniqueStrings);201forceDeduplication(ageThreshold, gcType);202verifyStrings(list, numberOfUniqueStrings);203204System.out.println("End: DeduplicationTest");205}206207public static OutputAnalyzer run(int numberOfStrings, int ageThreshold, String gcType, String... extraArgs) throws Exception {208String[] defaultArgs = new String[] {209"-XX:+UseStringDeduplication",210"-XX:StringDeduplicationAgeThreshold=" + ageThreshold,211DeduplicationTest.class.getName(),212"" + numberOfStrings,213"" + numberOfStrings / 2,214"" + ageThreshold,215gcType216};217218ArrayList<String> args = new ArrayList<String>();219args.addAll(Arrays.asList(extraArgs));220args.addAll(Arrays.asList(defaultArgs));221222return runTest(args.toArray(new String[args.size()]));223}224}225226private static class InternedTest {227public static void main(String[] args) {228// This test verifies that interned strings are always229// deduplicated when being interned, and never after230// being interned.231232System.out.println("Begin: InternedTest");233234final int ageThreshold = Integer.parseUnsignedInt(args[0]);235final String baseString = "DeduplicationTestString:" + InternedTest.class.getName();236237// Create duplicate of baseString238StringBuilder sb1 = new StringBuilder(baseString);239String dupString1 = sb1.toString();240if (getValue(dupString1) == getValue(baseString)) {241throw new RuntimeException("Values should not match");242}243244// Force baseString to be inspected for deduplication245// and be inserted into the deduplication hashtable.246forceDeduplication(ageThreshold, FullGC);247248// Wait for deduplication to occur249while (getValue(dupString1) != getValue(baseString)) {250System.out.println("Waiting...");251try {252Thread.sleep(100);253} catch (Exception e) {254throw new RuntimeException(e);255}256}257258// Create a new duplicate of baseString259StringBuilder sb2 = new StringBuilder(baseString);260String dupString2 = sb2.toString();261if (getValue(dupString2) == getValue(baseString)) {262throw new RuntimeException("Values should not match");263}264265// Intern the new duplicate266Object beforeInternedValue = getValue(dupString2);267String internedString = dupString2.intern();268if (internedString != dupString2) {269throw new RuntimeException("String should match");270}271if (getValue(internedString) != getValue(baseString)) {272throw new RuntimeException("Values should match");273}274275// Check original value of interned string, to make sure276// deduplication happened on the interned string and not277// on the base string278if (beforeInternedValue == getValue(baseString)) {279throw new RuntimeException("Values should not match");280}281282System.out.println("End: InternedTest");283}284285public static OutputAnalyzer run() throws Exception {286return runTest("-XX:+PrintGC",287"-XX:+PrintGCDetails",288"-XX:+UseStringDeduplication",289"-XX:+PrintStringDeduplicationStatistics",290"-XX:StringDeduplicationAgeThreshold=" + DefaultAgeThreshold,291InternedTest.class.getName(),292"" + DefaultAgeThreshold);293}294}295296/*297* Tests298*/299300private static final int LargeNumberOfStrings = 10000;301private static final int SmallNumberOfStrings = 10;302303private static final int MaxAgeThreshold = 15;304private static final int DefaultAgeThreshold = 3;305private static final int MinAgeThreshold = 1;306307private static final int TooLowAgeThreshold = MinAgeThreshold - 1;308private static final int TooHighAgeThreshold = MaxAgeThreshold + 1;309310public static void testYoungGC() throws Exception {311// Do young GC to age strings to provoke deduplication312OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,313DefaultAgeThreshold,314YoungGC,315"-XX:+PrintGC",316"-XX:+PrintStringDeduplicationStatistics");317output.shouldNotContain("Full GC");318output.shouldContain("GC pause (G1 Evacuation Pause) (young)");319output.shouldContain("GC concurrent-string-deduplication");320output.shouldContain("Deduplicated:");321output.shouldHaveExitValue(0);322}323324public static void testFullGC() throws Exception {325// Do full GC to age strings to provoke deduplication326OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,327DefaultAgeThreshold,328FullGC,329"-XX:+PrintGC",330"-XX:+PrintStringDeduplicationStatistics");331output.shouldNotContain("GC pause (G1 Evacuation Pause) (young)");332output.shouldContain("Full GC");333output.shouldContain("GC concurrent-string-deduplication");334output.shouldContain("Deduplicated:");335output.shouldHaveExitValue(0);336}337338public static void testTableResize() throws Exception {339// Test with StringDeduplicationResizeALot340OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,341DefaultAgeThreshold,342YoungGC,343"-XX:+PrintGC",344"-XX:+PrintStringDeduplicationStatistics",345"-XX:+StringDeduplicationResizeALot");346output.shouldContain("GC concurrent-string-deduplication");347output.shouldContain("Deduplicated:");348output.shouldNotContain("Resize Count: 0");349output.shouldHaveExitValue(0);350}351352public static void testTableRehash() throws Exception {353// Test with StringDeduplicationRehashALot354OutputAnalyzer output = DeduplicationTest.run(LargeNumberOfStrings,355DefaultAgeThreshold,356YoungGC,357"-XX:+PrintGC",358"-XX:+PrintStringDeduplicationStatistics",359"-XX:+StringDeduplicationRehashALot");360output.shouldContain("GC concurrent-string-deduplication");361output.shouldContain("Deduplicated:");362output.shouldNotContain("Rehash Count: 0");363output.shouldNotContain("Hash Seed: 0x0");364output.shouldHaveExitValue(0);365}366367public static void testAgeThreshold() throws Exception {368OutputAnalyzer output;369370// Test with max age theshold371output = DeduplicationTest.run(SmallNumberOfStrings,372MaxAgeThreshold,373YoungGC,374"-XX:+PrintGC",375"-XX:+PrintStringDeduplicationStatistics");376output.shouldContain("GC concurrent-string-deduplication");377output.shouldContain("Deduplicated:");378output.shouldHaveExitValue(0);379380// Test with min age theshold381output = DeduplicationTest.run(SmallNumberOfStrings,382MinAgeThreshold,383YoungGC,384"-XX:+PrintGC",385"-XX:+PrintStringDeduplicationStatistics");386output.shouldContain("GC concurrent-string-deduplication");387output.shouldContain("Deduplicated:");388output.shouldHaveExitValue(0);389390// Test with too low age threshold391output = DeduplicationTest.run(SmallNumberOfStrings,392TooLowAgeThreshold,393YoungGC);394output.shouldContain("StringDeduplicationAgeThreshold of " + TooLowAgeThreshold +395" is invalid; must be between " + MinAgeThreshold + " and " + MaxAgeThreshold);396output.shouldHaveExitValue(1);397398// Test with too high age threshold399output = DeduplicationTest.run(SmallNumberOfStrings,400TooHighAgeThreshold,401YoungGC);402output.shouldContain("StringDeduplicationAgeThreshold of " + TooHighAgeThreshold +403" is invalid; must be between " + MinAgeThreshold + " and " + MaxAgeThreshold);404output.shouldHaveExitValue(1);405}406407public static void testPrintOptions() throws Exception {408OutputAnalyzer output;409410// Test without PrintGC and without PrintStringDeduplicationStatistics411output = DeduplicationTest.run(SmallNumberOfStrings,412DefaultAgeThreshold,413YoungGC);414output.shouldNotContain("GC concurrent-string-deduplication");415output.shouldNotContain("Deduplicated:");416output.shouldHaveExitValue(0);417418// Test with PrintGC but without PrintStringDeduplicationStatistics419output = DeduplicationTest.run(SmallNumberOfStrings,420DefaultAgeThreshold,421YoungGC,422"-XX:+PrintGC");423output.shouldContain("GC concurrent-string-deduplication");424output.shouldNotContain("Deduplicated:");425output.shouldHaveExitValue(0);426}427428public static void testInterned() throws Exception {429// Test that interned strings are deduplicated before being interned430OutputAnalyzer output = InternedTest.run();431output.shouldHaveExitValue(0);432}433}434435436