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 * 29 * @run main/othervm -Xmx1g -verbose:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication 30 * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive 31 * -XX:+ShenandoahDegeneratedGC 32 * TestStringDedupStress 33 * 34 * @run main/othervm -Xmx1g -verbose:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication 35 * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive 36 * -XX:-ShenandoahDegeneratedGC 37 * TestStringDedupStress 38 */ 39 40 /* 41 * @test TestStringDedupStress 42 * @summary Test Shenandoah string deduplication implementation 43 * @key gc 44 * 45 * @run main/othervm -Xmx1g -verbose:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication 46 * -XX:+UseShenandoahGC 47 * -DtargetStrings=3000000 48 * TestStringDedupStress 49 * 50 * @run main/othervm -Xmx1g -verbose:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication 51 * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive 52 * -DtargetStrings=2000000 53 * TestStringDedupStress 54 * 55 * @run main/othervm -Xmx1g -verbose:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication 56 * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive 57 * -XX:+ShenandoahOOMDuringEvacALot 58 * -DtargetStrings=2000000 59 * TestStringDedupStress 60 * 61 * @run main/othervm -Xmx1g -verbose:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication 62 * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact 63 * TestStringDedupStress 64 */ 65 66 /* 67 * @test TestStringDedupStress 68 * @summary Test Shenandoah string deduplication implementation 69 * @key gc 70 * 71 * @run main/othervm -Xmx1g -verbose:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication 72 * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu 73 * TestStringDedupStress 74 * 75 * @run main/othervm -Xmx1g -verbose:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication 76 * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive 77 * -DtargetStrings=2000000 78 * TestStringDedupStress 79 * 80 * @run main/othervm -Xmx1g -verbose:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication 81 * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu 82 * -XX:+ShenandoahOOMDuringEvacALot 83 * -DtargetStrings=2000000 84 * TestStringDedupStress 85 * 86 * @run main/othervm -Xmx1g -verbose:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication 87 * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive 88 * -XX:+ShenandoahOOMDuringEvacALot 89 * -DtargetStrings=2000000 90 * TestStringDedupStress 91 */ 92 93 import java.lang.management.*; 94 import java.lang.reflect.*; 95 import java.util.*; 96 97 import sun.misc.*; 98 99 public class TestStringDedupStress { 100 private static Field valueField; 101 private static Unsafe unsafe; 102 103 private static final int TARGET_STRINGS = Integer.getInteger("targetStrings", 2_500_000); 104 private static final long MAX_REWRITE_GC_CYCLES = 6; 105 private static final long MAX_REWRITE_TIME = 30*1000; // ms 106 107 private static final int UNIQUE_STRINGS = 20; 108 109 static { 110 try { 111 Field field = Unsafe.class.getDeclaredField("theUnsafe"); 112 field.setAccessible(true); 113 unsafe = (Unsafe) field.get(null); 114 115 valueField = String.class.getDeclaredField("value"); 116 valueField.setAccessible(true); 117 } catch (Exception e) { 118 throw new RuntimeException(e); 119 } 120 } 121 122 private static Object getValue(String string) { 123 try { 124 return valueField.get(string); 125 } catch (Exception e) { 126 throw new RuntimeException(e); 127 } 128 } 129 130 static class StringAndId { 131 private String str; 132 private int id; 133 134 public StringAndId(String str, int id) { 135 this.str = str; 136 this.id = id; 137 } 138 139 public String str() { 140 return str; 141 } 142 143 public int id() { 144 return id; 145 } 146 } 147 148 // Generate uniqueStrings number of strings 149 private static void generateStrings(ArrayList<StringAndId> strs, int uniqueStrings) { 150 Random rn = new Random(); 151 for (int u = 0; u < uniqueStrings; u++) { 152 int n = rn.nextInt(uniqueStrings); 153 strs.add(new StringAndId("Unique String " + n, n)); 154 } 155 } 156 157 private static int verifyDedupString(ArrayList<StringAndId> strs) { 158 Map<Object, StringAndId> seen = new HashMap<>(TARGET_STRINGS*2); 159 int total = 0; 160 int dedup = 0; 161 162 for (StringAndId item : strs) { 163 total++; 164 StringAndId existingItem = seen.get(getValue(item.str())); 165 if (existingItem == null) { 166 seen.put(getValue(item.str()), item); 167 } else { 168 if (item.id() != existingItem.id() || 169 !item.str().equals(existingItem.str())) { 170 System.out.println("StringDedup error:"); 171 System.out.println("id: " + item.id() + " != " + existingItem.id()); 172 System.out.println("or String: " + item.str() + " != " + existingItem.str()); 173 throw new RuntimeException("StringDedup Test failed"); 174 } else { 175 dedup++; 176 } 177 } 178 } 179 System.out.println("Dedup: " + dedup + "/" + total + " unique: " + (total - dedup)); 180 return (total - dedup); 181 } 182 183 static volatile ArrayList<StringAndId> astrs = new ArrayList<>(); 184 static GarbageCollectorMXBean gcCycleMBean; 185 186 public static void main(String[] args) { 187 Random rn = new Random(); 188 189 for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { 190 if ("Shenandoah Cycles".equals(bean.getName())) { 191 gcCycleMBean = bean; 192 break; 193 } 194 } 195 196 if (gcCycleMBean == null) { 197 throw new RuntimeException("Can not find Shenandoah GC cycle mbean"); 198 } 199 200 // Generate roughly TARGET_STRINGS strings, only UNIQUE_STRINGS are unique 201 int genIters = TARGET_STRINGS / UNIQUE_STRINGS; 202 for (int index = 0; index < genIters; index++) { 203 generateStrings(astrs, UNIQUE_STRINGS); 204 } 205 206 long cycleBeforeRewrite = gcCycleMBean.getCollectionCount(); 207 long timeBeforeRewrite = System.currentTimeMillis(); 208 209 long loop = 1; 210 while (true) { 211 int arrSize = astrs.size(); 212 int index = rn.nextInt(arrSize); 213 StringAndId item = astrs.get(index); 214 int n = rn.nextInt(UNIQUE_STRINGS); 215 item.str = "Unique String " + n; 216 item.id = n; 217 218 if (loop++ % 1000 == 0) { 219 // enough GC cycles for rewritten strings to be deduplicated 220 if (gcCycleMBean.getCollectionCount() - cycleBeforeRewrite >= MAX_REWRITE_GC_CYCLES) { 221 break; 222 } 223 224 // enough time is spent waiting for GC to happen 225 if (System.currentTimeMillis() - timeBeforeRewrite >= MAX_REWRITE_TIME) { 226 break; 227 } 228 } 229 } 230 verifyDedupString(astrs); 231 } 232 }