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