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 /*
 84  * @test id=iu
 85  * @summary Test Shenandoah string deduplication implementation
 86  * @key randomness
 87  * @requires vm.gc.Shenandoah
 88  * @library /test/lib
 89  * @modules java.base/java.lang:open
 90  *          java.management
 91  *
 92  * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication
 93  *      -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:StringDeduplicationAgeThreshold=3
 94  *      TestStringDedup
 95  *
 96  * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication
 97  *      -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive -XX:StringDeduplicationAgeThreshold=3
 98  *      TestStringDedup
 99  */
100 
101 import java.lang.reflect.*;
102 import java.util.*;
103 import jdk.test.lib.Utils;
104 
105 public class TestStringDedup {
106     private static Field valueField;
107 
108     private static final int UniqueStrings = 20;
109     // How many GC cycles are needed to complete deduplication.
110     private static final int GCCount = Integer.getInteger("GCCount", 3);
111 
112     static {
113         try {
114             valueField = String.class.getDeclaredField("value");
115             valueField.setAccessible(true);
116         } catch (Exception e) {
117             throw new RuntimeException(e);
118         }
119     }
120 
121     private static Object getValue(String string) {
122         try {
123             return valueField.get(string);
124         } catch (Exception e) {
125             throw new RuntimeException(e);
126         }
127     }
128 
129     static class StringAndId {
130         private String str;
131         private int id;
132 
133         public StringAndId(String str, int id) {
134             this.str = str;
135             this.id = id;
136         }
137 
138         public String str() {
139             return str;
140         }
141 
142         public int id() {
143             return id;
144         }
145     }
146 
147     private static void generateStrings(ArrayList<StringAndId> strs, int unique_strs) {
148         Random rn = Utils.getRandomInstance();
149         for (int u = 0; u < unique_strs; u++) {
150             int n = rn.nextInt() % 10;
151             n = Math.max(n, 2);
152             for (int index = 0; index < n; index++) {
153                 strs.add(new StringAndId("Unique String " + u, u));
154             }
155         }
156     }
157 
158     private static int verifyDedepString(ArrayList<StringAndId> strs) {
159         HashMap<Object, StringAndId> seen = new HashMap<>();
160         int total = 0;
161         int dedup = 0;
162 
163         for (StringAndId item : strs) {
164             total++;
165             StringAndId existing_item = seen.get(getValue(item.str()));
166             if (existing_item == null) {
167                 seen.put(getValue(item.str()), item);
168             } else {
169                 if (item.id() != existing_item.id() ||
170                         !item.str().equals(existing_item.str())) {
171                     System.out.println("StringDedup error:");
172                     System.out.println("String: " + item.str() + " != " + existing_item.str());
173                     throw new RuntimeException("StringDedup Test failed");
174                 } else {
175                     dedup++;
176                 }
177             }
178         }
179         return (total - dedup);
180     }
181 
182     public static void main(String[] args) {
183         ArrayList<StringAndId> astrs = new ArrayList<>();
184         generateStrings(astrs, UniqueStrings);
185         for (int count = 0; count < GCCount; count ++) {
186           System.gc();
187         }
188 
189         int unique_count = 0;
190         for (int waitCount = 0; waitCount < 3; waitCount ++) {
191             // Let concurrent string dedup thread to run
192             try {
193                 Thread.sleep(1000);
194             } catch (InterruptedException e) {
195             }
196 
197             // All deduplicated, done.
198             unique_count = verifyDedepString(astrs);
199             if ( unique_count == UniqueStrings) {
200                 return;
201             }
202         }
203 
204         throw new RuntimeException("Expecting " + UniqueStrings + " unique strings, but got " + unique_count);
205     }
206 }