1 /*
   2  * Copyright (c) 2017, 2018 Red Hat, Inc. and/or its affiliates.
   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  *
  29  * @run main/othervm -XX:+UseShenandoahGC -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCHeuristics=passive      -XX:+ShenandoahDegeneratedGC -XX:+UseStringDeduplication -Xmx256M TestShenandoahStrDedup
  30  * @run main/othervm -XX:+UseShenandoahGC -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCHeuristics=passive      -XX:-ShenandoahDegeneratedGC -XX:+UseStringDeduplication -Xmx256M TestShenandoahStrDedup
  31  *
  32  * @run main/othervm -XX:+UseShenandoahGC -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions                                         -XX:+UseStringDeduplication -Xmx256M TestShenandoahStrDedup
  33  * @run main/othervm -XX:+UseShenandoahGC -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCHeuristics=aggressive   -XX:+UseStringDeduplication -Xmx256M TestShenandoahStrDedup
  34  * @run main/othervm -XX:+UseShenandoahGC -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCHeuristics=compact      -XX:+UseStringDeduplication -Xmx256M TestShenandoahStrDedup
  35  */
  36 
  37 import java.lang.reflect.*;
  38 import java.util.*;
  39 import sun.misc.*;
  40 
  41 public class TestShenandoahStrDedup {
  42   private static Field valueField;
  43   private static Unsafe unsafe;
  44 
  45   private static final int UniqueStrings = 20;
  46   static {
  47     try {
  48       Field field = Unsafe.class.getDeclaredField("theUnsafe");
  49       field.setAccessible(true);
  50       unsafe = (Unsafe)field.get(null);
  51 
  52       valueField = String.class.getDeclaredField("value");
  53       valueField.setAccessible(true);
  54     } catch (Exception e) {
  55       throw new RuntimeException(e);
  56     }
  57   }
  58 
  59   private static Object getValue(String string) {
  60     try {
  61       return valueField.get(string);
  62     } catch (Exception e) {
  63         throw new RuntimeException(e);
  64     }
  65   }
  66 
  67   static class StringAndId {
  68     private String str;
  69     private int    id;
  70     public StringAndId(String str, int id) {
  71       this.str = str;
  72       this.id = id;
  73     }
  74 
  75     public String str() { return str; }
  76     public int    id()  { return id;  }
  77   }
  78 
  79   private static void generateStrings(ArrayList<StringAndId> strs, int unique_strs) {
  80     Random rn = new Random();
  81     for (int u = 0; u < unique_strs; u ++) {
  82       int n = rn.nextInt() % 10;
  83       n = Math.max(n, 2);
  84       for (int index = 0; index < n; index ++) {
  85           strs.add(new StringAndId("Unique String " + u, u));
  86       }
  87     }
  88   }
  89 
  90   private static int verifyDedepString(ArrayList<StringAndId> strs) {
  91     HashMap<Object, StringAndId> seen = new HashMap<>();
  92     int total = 0;
  93     int dedup = 0;
  94 
  95     for (StringAndId item : strs) {
  96       total ++;
  97       StringAndId existing_item = seen.get(getValue(item.str()));
  98       if (existing_item == null) {
  99         seen.put(getValue(item.str()), item);
 100       } else {
 101         if (item.id() != existing_item.id() ||
 102             !item.str().equals(existing_item.str())) {
 103           System.out.println("StringDedup error:");
 104           System.out.println("String: " + item.str() + " != " + existing_item.str());
 105           throw new RuntimeException("StringDedup Test failed");
 106         } else {
 107           dedup ++;
 108         }
 109       }
 110     }
 111     System.out.println("Dedup: " + dedup + "/" + total + " unique: "  + (total - dedup));
 112     return (total - dedup);
 113   }
 114 
 115   public static void main(String[] args) {
 116     ArrayList<StringAndId> astrs = new ArrayList<>();
 117     generateStrings(astrs, UniqueStrings);
 118     System.gc();
 119     System.gc();
 120     System.gc();
 121     System.gc();
 122     System.gc();
 123 
 124     if (verifyDedepString(astrs) != UniqueStrings) {
 125       // Can not guarantee all strings are deduplicated, there can
 126       // still have pending items in queues.
 127       System.out.println("Not all strings are deduplicated");
 128     }
 129   }
 130 }