1 /*
  2  * Copyright (c) 2017, 2021, Red Hat, Inc. All rights reserved.
  3  * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
  4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  5  *
  6  * This code is free software; you can redistribute it and/or modify it
  7  * under the terms of the GNU General Public License version 2 only, as
  8  * published by the Free Software Foundation.
  9  *
 10  * This code is distributed in the hope that it will be useful, but WITHOUT
 11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 13  * version 2 for more details (a copy is included in the LICENSE file that
 14  * accompanied this code).
 15  *
 16  * You should have received a copy of the GNU General Public License version
 17  * 2 along with this work; if not, write to the Free Software Foundation,
 18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 19  *
 20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 21  * or visit www.oracle.com if you need additional information or have any
 22  * questions.
 23  *
 24  */
 25 
 26 /*
 27  * @test id=passive
 28  * @summary Test Shenandoah string deduplication implementation
 29  * @key randomness
 30  * @requires vm.gc.Shenandoah
 31  * @library /test/lib
 32  * @modules java.base/jdk.internal.misc:open
 33  * @modules java.base/java.lang:open
 34  *          java.management
 35  *
 36  * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication
 37  *      -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive
 38  *      -XX:+ShenandoahDegeneratedGC -DGCCount=1
 39  *      TestStringDedup
 40  *
 41  * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication
 42  *      -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive
 43  *      -XX:-ShenandoahDegeneratedGC -DGCCount=1
 44  *      TestStringDedup
 45  */
 46 
 47 /*
 48  * @test id=default
 49  * @summary Test Shenandoah string deduplication implementation
 50  * @key randomness
 51  * @requires vm.gc.Shenandoah
 52  * @library /test/lib
 53  * @modules java.base/java.lang:open
 54  *          java.management
 55  *
 56  * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication
 57  *      -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive -XX:StringDeduplicationAgeThreshold=3
 58  *      TestStringDedup
 59  *
 60  * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication
 61  *      -XX:+UseShenandoahGC -XX:StringDeduplicationAgeThreshold=3
 62  *      TestStringDedup
 63  *
 64  * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication
 65  *      -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact -XX:StringDeduplicationAgeThreshold=3
 66  *      TestStringDedup
 67  */
 68 
 69 /*
 70  * @test id=generational
 71  * @summary Test Shenandoah string deduplication implementation
 72  * @key randomness
 73  * @requires vm.gc.Shenandoah
 74  * @library /test/lib
 75  * @modules java.base/java.lang:open
 76  *          java.management
 77  *
 78  * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication
 79  *      -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:StringDeduplicationAgeThreshold=3
 80  *      TestStringDedup
 81  */
 82 
 83 import java.lang.reflect.*;
 84 import java.util.*;
 85 import jdk.test.lib.Utils;
 86 
 87 public class TestStringDedup {
 88     private static Field valueField;
 89 
 90     private static final int UniqueStrings = 20;
 91     // How many GC cycles are needed to complete deduplication.
 92     private static final int GCCount = Integer.getInteger("GCCount", 3);
 93 
 94     static {
 95         try {
 96             valueField = String.class.getDeclaredField("value");
 97             valueField.setAccessible(true);
 98         } catch (Exception e) {
 99             throw new RuntimeException(e);
100         }
101     }
102 
103     private static Object getValue(String string) {
104         try {
105             return valueField.get(string);
106         } catch (Exception e) {
107             throw new RuntimeException(e);
108         }
109     }
110 
111     static class StringAndId {
112         private String str;
113         private int id;
114 
115         public StringAndId(String str, int id) {
116             this.str = str;
117             this.id = id;
118         }
119 
120         public String str() {
121             return str;
122         }
123 
124         public int id() {
125             return id;
126         }
127     }
128 
129     private static void generateStrings(ArrayList<StringAndId> strs, int unique_strs) {
130         Random rn = Utils.getRandomInstance();
131         for (int u = 0; u < unique_strs; u++) {
132             int n = rn.nextInt() % 10;
133             n = Math.max(n, 2);
134             for (int index = 0; index < n; index++) {
135                 strs.add(new StringAndId("Unique String " + u, u));
136             }
137         }
138     }
139 
140     private static int verifyDedepString(ArrayList<StringAndId> strs) {
141         HashMap<Object, StringAndId> seen = new HashMap<>();
142         int total = 0;
143         int dedup = 0;
144 
145         for (StringAndId item : strs) {
146             total++;
147             StringAndId existing_item = seen.get(getValue(item.str()));
148             if (existing_item == null) {
149                 seen.put(getValue(item.str()), item);
150             } else {
151                 if (item.id() != existing_item.id() ||
152                         !item.str().equals(existing_item.str())) {
153                     System.out.println("StringDedup error:");
154                     System.out.println("String: " + item.str() + " != " + existing_item.str());
155                     throw new RuntimeException("StringDedup Test failed");
156                 } else {
157                     dedup++;
158                 }
159             }
160         }
161         return (total - dedup);
162     }
163 
164     public static void main(String[] args) {
165         ArrayList<StringAndId> astrs = new ArrayList<>();
166         generateStrings(astrs, UniqueStrings);
167         for (int count = 0; count < GCCount; count ++) {
168           System.gc();
169         }
170 
171         int unique_count = 0;
172         for (int waitCount = 0; waitCount < 3; waitCount ++) {
173             // Let concurrent string dedup thread to run
174             try {
175                 Thread.sleep(1000);
176             } catch (InterruptedException e) {
177             }
178 
179             // All deduplicated, done.
180             unique_count = verifyDedepString(astrs);
181             if ( unique_count == UniqueStrings) {
182                 return;
183             }
184         }
185 
186         throw new RuntimeException("Expecting " + UniqueStrings + " unique strings, but got " + unique_count);
187     }
188 }