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 #ifndef SHARE_VM_GC_SHENANDOAHSTRDEDUPTABLE_HPP
  25 #define SHARE_VM_GC_SHENANDOAHSTRDEDUPTABLE_HPP
  26 
  27 #include "utilities/ostream.hpp"
  28 
  29 class ShenandoahStrDedupEntry : public CHeapObj<mtGC> {
  30 private:
  31   ShenandoahStrDedupEntry* volatile    _next;
  32   unsigned int                         _hash;
  33   typeArrayOop                         _obj;
  34 
  35 public:
  36   ShenandoahStrDedupEntry() : _next(NULL), _hash(0), _obj(NULL) {
  37   }
  38 
  39   ShenandoahStrDedupEntry* volatile next() {
  40     return _next;
  41   }
  42 
  43   ShenandoahStrDedupEntry* volatile* next_addr() {
  44     return &_next;
  45   }
  46 
  47   void set_next(ShenandoahStrDedupEntry* next) {
  48     _next = next;
  49   }
  50 
  51   bool cas_set_next(ShenandoahStrDedupEntry* next);
  52 
  53   unsigned int hash() const {
  54     return _hash;
  55   }
  56 
  57   void set_hash(unsigned int hash) {
  58     _hash = hash;
  59   }
  60 
  61   typeArrayOop obj() const {
  62     return _obj;
  63   }
  64 
  65   typeArrayOop* obj_addr() {
  66     return &_obj;
  67   }
  68 
  69   void set_obj(typeArrayOop obj) {
  70     _obj = obj;
  71   }
  72 
  73   bool equals(typeArrayOop value, unsigned int hash) const {
  74     return (hash == this->hash() &&
  75             equals(value, obj()));
  76   }
  77 
  78   void do_oop(OopClosure* cl) {
  79     oop* p = (oop*)obj_addr();
  80     cl->do_oop(p);
  81   }
  82 
  83 private:
  84   static bool equals(typeArrayOop value1, typeArrayOop value2) {
  85     return (value1 == value2 ||
  86             (value1->length() == value2->length() &&
  87             (!memcmp(value1->base(T_CHAR),
  88                      value2->base(T_CHAR),
  89                      value1->length() * sizeof(jchar)))));
  90   }
  91 };
  92 
  93 /* ShenandoahStringDedupTable:
  94  *  - Lookup and add are lock free
  95  *  - Cleanup, resize and rehash are at safepoints
  96  */
  97 class ShenandoahStrDedupTable : public CHeapObj<mtGC> {
  98   friend class ShenandoahStrDedupTableUnlinkTask;
  99   friend class ShenandoahStrDedupTableRehashTask;
 100   friend class ShenandoahStrDedupShrinkTableTask;
 101   friend class ShenandoahStrDedupExpandTableTask;
 102 
 103 private:
 104   ShenandoahStrDedupEntry* volatile *    _buckets;
 105   size_t                                 _size;
 106   volatile size_t                        _entries;
 107 
 108   uintx                                  _shrink_threshold;
 109   uintx                                  _grow_threshold;
 110   bool                                   _rehash_needed;
 111 
 112   // The hash seed also dictates which hash function to use. A
 113   // zero hash seed means we will use the Java compatible hash
 114   // function (which doesn't use a seed), and a non-zero hash
 115   // seed means we use the murmur3 hash function.
 116   jint                            _hash_seed;
 117 
 118   // Constants governing table resize/rehash/cache.
 119   static const size_t             _min_size;
 120   static const size_t             _max_size;
 121   static const double             _grow_load_factor;
 122   static const double             _shrink_load_factor;
 123   static const uintx              _rehash_multiple;
 124   static const uintx              _rehash_threshold;
 125   static const double             _max_cache_factor;
 126 
 127   volatile size_t _claimed;
 128   size_t          _partition_size;
 129 
 130 public:
 131   ShenandoahStrDedupTable(size_t size = _min_size, jint hash_seed = 0);
 132   ~ShenandoahStrDedupTable();
 133 
 134   jint hash_seed() const    { return _hash_seed; }
 135   size_t size()    const    { return _size; }
 136   bool need_rehash() const  { return _rehash_needed; }
 137   bool need_expand() const  { return _entries >= _grow_threshold && size() < max_size(); }
 138   bool need_shrink() const  { return _entries <= _shrink_threshold && size() > min_size(); }
 139 
 140   // parallel scanning the table
 141   void clear_claimed();
 142   size_t claim();
 143   void parallel_oops_do(OopClosure* cl);
 144 
 145   // For verification only
 146   void oops_do_slow(OopClosure* cl);
 147 
 148   bool deduplicate(oop java_string);
 149 
 150   // Returns an existing character array in the table, or inserts a new
 151   // table entry if no matching character array exists.
 152   typeArrayOop lookup_or_add(typeArrayOop value, unsigned int hash, uintx& count);
 153 
 154   void print_statistics(outputStream* out) const;
 155 
 156   static size_t min_size() { return _min_size; }
 157   static size_t max_size() { return _max_size; }
 158 
 159   void verify() PRODUCT_RETURN;
 160 
 161 private:
 162   inline bool use_java_hash() {
 163     return _hash_seed == 0;
 164   }
 165 
 166   // Returns the hash bucket index for the given hash code.
 167   size_t hash_to_index(unsigned int hash) {
 168     return (size_t)hash & (size() - 1);
 169   }
 170 
 171   ShenandoahStrDedupEntry* volatile * bucket_addr(size_t index) const {
 172     assert(index < size(), "Index out of bound");
 173     return &_buckets[index];
 174   }
 175 
 176   // Returns the hash bucket at the given index.
 177   ShenandoahStrDedupEntry* volatile bucket(size_t index) const {
 178     assert(index < size(), "Index out of bound");
 179     return _buckets[index];
 180   }
 181 
 182   size_t partition_size() const { return _partition_size; }
 183 
 184   ShenandoahStrDedupEntry* allocate_entry(typeArrayOop value, unsigned int hash);
 185   void release_entry(ShenandoahStrDedupEntry* entry);
 186 
 187   unsigned int hash_code(oop java_string, typeArrayOop value);
 188   unsigned int java_hash_code(typeArrayOop value);
 189   unsigned int alt_hash_code(typeArrayOop value);
 190 
 191   // Adds a new table entry to the given hash bucket.
 192   void add(ShenandoahStrDedupEntry* entry);
 193 
 194   // Clean up a bucket, return number of entries removed
 195   size_t cleanup_bucket(size_t index);
 196 };
 197 
 198 class ShenandoahHeap;
 199 
 200 class ShenandoahStrDedupTableCleanupTask : public CHeapObj<mtGC> {
 201 private:
 202   ShenandoahMarkingContext* const _mark_context;
 203 
 204 public:
 205   ShenandoahStrDedupTableCleanupTask();
 206   virtual ~ShenandoahStrDedupTableCleanupTask() {};
 207   virtual void do_parallel_cleanup() = 0;
 208 
 209 protected:
 210   bool is_alive(oop obj) const;
 211 };
 212 
 213 // Cleanup current string dedup table, remove all dead entries
 214 class ShenandoahStrDedupTableUnlinkTask : public ShenandoahStrDedupTableCleanupTask {
 215 private:
 216   ShenandoahStrDedupTable* const  _table;
 217 
 218 public:
 219   ShenandoahStrDedupTableUnlinkTask(ShenandoahStrDedupTable* const table);
 220   void do_parallel_cleanup();
 221 };
 222 
 223 // The task transfers live entries from source table to destination table
 224 class ShenandoahStrDedupTableRemapTask : public ShenandoahStrDedupTableCleanupTask {
 225 protected:
 226   ShenandoahStrDedupTable* const  _src_table;
 227   ShenandoahStrDedupTable* const  _dest_table;
 228 
 229 public:
 230   ShenandoahStrDedupTableRemapTask(ShenandoahStrDedupTable* const src,
 231                                    ShenandoahStrDedupTable* const dest);
 232 protected:
 233   ShenandoahStrDedupTable* const src_table()  const { return _src_table; }
 234   ShenandoahStrDedupTable* const dest_table() const { return _dest_table; }
 235 };
 236 
 237 // The task rehashes live entries from source table to destination table.
 238 // Source and destination tables are not necessary the same size.
 239 class ShenandoahStrDedupTableRehashTask : public ShenandoahStrDedupTableRemapTask {
 240 public:
 241   ShenandoahStrDedupTableRehashTask(ShenandoahStrDedupTable* const src,
 242                                     ShenandoahStrDedupTable* const dest);
 243   void do_parallel_cleanup();
 244 };
 245 
 246 /* The task remaps live entries from source table into destination table of
 247  * the half size.
 248  * Hash function should *not* be changed during shrinking of the table,
 249  * so we can merge buckets from source table into destination table.
 250  *  bucket [index ] and bucket [index + half_table_size] -> bucket [index]
 251  */
 252 class ShenandoahStrDedupShrinkTableTask : public ShenandoahStrDedupTableRemapTask {
 253 public:
 254   ShenandoahStrDedupShrinkTableTask(ShenandoahStrDedupTable* const src,
 255                                     ShenandoahStrDedupTable* const dest);
 256   void do_parallel_cleanup();
 257 
 258 protected:
 259   size_t transfer_bucket(ShenandoahStrDedupEntry* volatile src,
 260                          ShenandoahStrDedupEntry* volatile * dest);
 261 };
 262 
 263 /* The task remaps live entries from source table into destination table of
 264  * twice the size.
 265  * Hash function should *not* be changed during shrinking of the table,
 266  * so we can split buckets from source table into destination table.
 267  *  bucket [index ] -> bucket [index] or bucket [index + half_table_size]
 268  */
 269 class ShenandoahStrDedupExpandTableTask : public ShenandoahStrDedupTableRemapTask {
 270 private:
 271   int    _bit_mask;
 272 
 273 public:
 274   ShenandoahStrDedupExpandTableTask(ShenandoahStrDedupTable* const src,
 275                                     ShenandoahStrDedupTable* const dest);
 276   void do_parallel_cleanup();
 277 
 278 protected:
 279   size_t split_bucket(ShenandoahStrDedupEntry* volatile src,
 280     ShenandoahStrDedupEntry* volatile * dest_low,
 281     ShenandoahStrDedupEntry* volatile * dest_high);
 282 };
 283 
 284 #endif // SHARE_VM_GC_SHENANDOAHSTRDEDUPTABLE_HPP