1 /* 2 * Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. 7 * 8 * This code is distributed in the hope that it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 11 * version 2 for more details (a copy is included in the LICENSE file that 12 * accompanied this code). 13 * 14 * You should have received a copy of the GNU General Public License version 15 * 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 USA 19 * or visit www.oracle.com if you need additional information or have any 20 * questions. 21 * 22 */ 23 24 /* 25 * @test TestStringDedupStress 26 * @summary Test Shenandoah string deduplication implementation 27 * @key gc 28 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 29 * -verbose:gc 30 * -DtargetStrings=3000000 31 * TestStringDedupStress 32 * 33 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 34 * -XX:ShenandoahGCHeuristics=aggressive -DtargetStrings=2000000 35 * -verbose:gc 36 * TestStringDedupStress 37 * 38 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 39 * -XX:ShenandoahGCHeuristics=aggressive -XX:+ShenandoahOOMDuringEvacALot -DtargetStrings=2000000 40 * -verbose:gc 41 * TestStringDedupStress 42 * 43 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 44 * -XX:ShenandoahGCHeuristics=static -DtargetStrings=4000000 45 * -verbose:gc 46 * TestStringDedupStress 47 * 48 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 49 * -XX:ShenandoahGCHeuristics=compact 50 * -verbose:gc 51 * TestStringDedupStress 52 * 53 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 54 * -XX:ShenandoahGCMode=passive -XX:+ShenandoahDegeneratedGC -DtargetOverwrites=40000000 55 * -verbose:gc 56 * TestStringDedupStress 57 * 58 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 59 * -XX:ShenandoahGCMode=passive -XX:-ShenandoahDegeneratedGC -DtargetOverwrites=40000000 60 * -verbose:gc 61 * TestStringDedupStress 62 * 63 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 64 * -XX:ShenandoahUpdateRefsEarly=off -DtargetStrings=3000000 65 * -verbose:gc 66 * TestStringDedupStress 67 * 68 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 69 * -XX:ShenandoahGCHeuristics=compact -XX:ShenandoahUpdateRefsEarly=off -DtargetStrings=2000000 70 * -verbose:gc 71 * TestStringDedupStress 72 * 73 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 74 * -XX:ShenandoahGCHeuristics=aggressive -XX:ShenandoahUpdateRefsEarly=off -DtargetStrings=2000000 75 * -verbose:gc 76 * TestStringDedupStress 77 * 78 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 79 * -XX:ShenandoahGCHeuristics=static -XX:ShenandoahUpdateRefsEarly=off -DtargetOverwrites=4000000 80 * -verbose:gc 81 * TestStringDedupStress 82 * 83 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 84 * -XX:ShenandoahGCHeuristics=aggressive -XX:ShenandoahUpdateRefsEarly=off -XX:+ShenandoahOOMDuringEvacALot -DtargetStrings=2000000 85 * -verbose:gc 86 * TestStringDedupStress 87 * 88 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 89 * -XX:ShenandoahGCMode=traversal 90 * -verbose:gc 91 * TestStringDedupStress 92 * 93 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 94 * -XX:ShenandoahGCMode=traversal -XX:ShenandoahGCHeuristics=aggressive -DtargetStrings=2000000 95 * -verbose:gc 96 * TestStringDedupStress 97 * 98 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 99 * -XX:ShenandoahGCMode=traversal 100 * -XX:+ShenandoahOOMDuringEvacALot 101 * -DtargetStrings=2000000 102 * -verbose:gc 103 * TestStringDedupStress 104 * 105 * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:+UseStringDeduplication -Xmx1g 106 * -XX:ShenandoahGCMode=traversal -XX:ShenandoahGCHeuristics=aggressive 107 * -XX:+ShenandoahOOMDuringEvacALot 108 * -DtargetStrings=2000000 109 * -verbose:gc 110 * TestStringDedupStress 111 */ 112 113 import java.lang.management.*; 114 import java.lang.reflect.*; 115 import java.util.*; 116 117 import sun.misc.*; 118 119 public class TestStringDedupStress { 120 private static Field valueField; 121 private static Unsafe unsafe; 122 123 private static final int TARGET_STRINGS = Integer.getInteger("targetStrings", 2_500_000); 124 private static final long MAX_REWRITE_GC_CYCLES = 6; 125 private static final long MAX_REWRITE_TIME = 30*1000; // ms 126 127 private static final int UNIQUE_STRINGS = 20; 128 129 static { 130 try { 131 Field field = Unsafe.class.getDeclaredField("theUnsafe"); 132 field.setAccessible(true); 133 unsafe = (Unsafe) field.get(null); 134 135 valueField = String.class.getDeclaredField("value"); 136 valueField.setAccessible(true); 137 } catch (Exception e) { 138 throw new RuntimeException(e); 139 } 140 } 141 142 private static Object getValue(String string) { 143 try { 144 return valueField.get(string); 145 } catch (Exception e) { 146 throw new RuntimeException(e); 147 } 148 } 149 150 static class StringAndId { 151 private String str; 152 private int id; 153 154 public StringAndId(String str, int id) { 155 this.str = str; 156 this.id = id; 157 } 158 159 public String str() { 160 return str; 161 } 162 163 public int id() { 164 return id; 165 } 166 } 167 168 // Generate uniqueStrings number of strings 169 private static void generateStrings(ArrayList<StringAndId> strs, int uniqueStrings) { 170 Random rn = new Random(); 171 for (int u = 0; u < uniqueStrings; u++) { 172 int n = rn.nextInt(uniqueStrings); 173 strs.add(new StringAndId("Unique String " + n, n)); 174 } 175 } 176 177 private static int verifyDedupString(ArrayList<StringAndId> strs) { 178 Map<Object, StringAndId> seen = new HashMap<>(TARGET_STRINGS*2); 179 int total = 0; 180 int dedup = 0; 181 182 for (StringAndId item : strs) { 183 total++; 184 StringAndId existingItem = seen.get(getValue(item.str())); 185 if (existingItem == null) { 186 seen.put(getValue(item.str()), item); 187 } else { 188 if (item.id() != existingItem.id() || 189 !item.str().equals(existingItem.str())) { 190 System.out.println("StringDedup error:"); 191 System.out.println("id: " + item.id() + " != " + existingItem.id()); 192 System.out.println("or String: " + item.str() + " != " + existingItem.str()); 193 throw new RuntimeException("StringDedup Test failed"); 194 } else { 195 dedup++; 196 } 197 } 198 } 199 System.out.println("Dedup: " + dedup + "/" + total + " unique: " + (total - dedup)); 200 return (total - dedup); 201 } 202 203 static volatile ArrayList<StringAndId> astrs = new ArrayList<>(); 204 static GarbageCollectorMXBean gcCycleMBean; 205 206 public static void main(String[] args) { 207 Random rn = new Random(); 208 209 for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { 210 if ("Shenandoah Cycles".equals(bean.getName())) { 211 gcCycleMBean = bean; 212 break; 213 } 214 } 215 216 if (gcCycleMBean == null) { 217 throw new RuntimeException("Can not find Shenandoah GC cycle mbean"); 218 } 219 220 // Generate roughly TARGET_STRINGS strings, only UNIQUE_STRINGS are unique 221 int genIters = TARGET_STRINGS / UNIQUE_STRINGS; 222 for (int index = 0; index < genIters; index++) { 223 generateStrings(astrs, UNIQUE_STRINGS); 224 } 225 226 long cycleBeforeRewrite = gcCycleMBean.getCollectionCount(); 227 long timeBeforeRewrite = System.currentTimeMillis(); 228 229 long loop = 1; 230 while (true) { 231 int arrSize = astrs.size(); 232 int index = rn.nextInt(arrSize); 233 StringAndId item = astrs.get(index); 234 int n = rn.nextInt(UNIQUE_STRINGS); 235 item.str = "Unique String " + n; 236 item.id = n; 237 238 if (loop++ % 1000 == 0) { 239 // enough GC cycles for rewritten strings to be deduplicated 240 if (gcCycleMBean.getCollectionCount() - cycleBeforeRewrite >= MAX_REWRITE_GC_CYCLES) { 241 break; 242 } 243 244 // enough time is spent waiting for GC to happen 245 if (System.currentTimeMillis() - timeBeforeRewrite >= MAX_REWRITE_TIME) { 246 break; 247 } 248 } 249 } 250 verifyDedupString(astrs); 251 } 252 }