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