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 #include "precompiled.hpp"
  25 
  26 #include "classfile/altHashing.hpp"
  27 #include "classfile/javaClasses.hpp"
  28 #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp"
  29 #include "gc_implementation/shenandoah/shenandoahHeap.inline.hpp"
  30 #include "gc_implementation/shenandoah/shenandoahLogging.hpp"
  31 #include "gc_implementation/shenandoah/shenandoahStrDedupTable.hpp"
  32 #include "memory/allocation.hpp"
  33 #include "runtime/atomic.hpp"
  34 #include "runtime/safepoint.hpp"
  35 
  36 
  37 const size_t  ShenandoahStrDedupTable::_min_size = (1 << 10);   // 1024
  38 const size_t  ShenandoahStrDedupTable::_max_size = (1 << 24);   // 16777216
  39 const double  ShenandoahStrDedupTable::_grow_load_factor = 2.0; // Grow table at 200% load
  40 const double  ShenandoahStrDedupTable::_shrink_load_factor = _grow_load_factor / 3.0; // Shrink table at 67% load
  41 const double  ShenandoahStrDedupTable::_max_cache_factor = 0.1; // Cache a maximum of 10% of the table size
  42 const uintx   ShenandoahStrDedupTable::_rehash_multiple = 60;   // Hash bucket has 60 times more collisions than expected
  43 const uintx   ShenandoahStrDedupTable::_rehash_threshold = (uintx)(_rehash_multiple * _grow_load_factor);
  44 
  45 
  46 bool ShenandoahStrDedupEntry::cas_set_next(ShenandoahStrDedupEntry* next) {
  47   return Atomic::cmpxchg_ptr(next, &_next, (ShenandoahStrDedupEntry*)NULL) == NULL;
  48 }
  49 
  50 ShenandoahStrDedupTable::ShenandoahStrDedupTable(size_t size, jint hash_seed)  :
  51   _size(size), _hash_seed(hash_seed), _entries(0), _claimed(0), _partition_size(0),
  52   _rehash_needed(false), _shrink_threshold((uintx)(size * _shrink_load_factor)),
  53   _grow_threshold((uintx)(size * _grow_load_factor))
  54 {
  55   assert(size >= _min_size && size <= _max_size, "Invalid table size");
  56   _buckets = NEW_C_HEAP_ARRAY(ShenandoahStrDedupEntry* volatile, size, mtGC);
  57   for (size_t index = 0; index < size; index ++) {
  58     _buckets[index] = NULL;
  59   }
  60 }
  61 
  62 ShenandoahStrDedupTable::~ShenandoahStrDedupTable() {
  63   for (size_t index = 0; index < size(); index ++) {
  64     ShenandoahStrDedupEntry* volatile head = bucket(index);
  65     ShenandoahStrDedupEntry* volatile tmp;
  66     while (head != NULL) {
  67       tmp = head;
  68       head = head->next();
  69       release_entry(tmp);
  70     }
  71   }
  72 }
  73 
  74 typeArrayOop ShenandoahStrDedupTable::lookup_or_add(typeArrayOop value, unsigned int hash, uintx& count) {
  75   ShenandoahStrDedupEntry* volatile* head_addr = bucket_addr(hash_to_index(hash));
  76   count = 0;
  77   ShenandoahStrDedupEntry* new_entry = NULL;
  78   if (*head_addr == NULL) {
  79     new_entry = allocate_entry(value, hash);
  80     if (Atomic::cmpxchg_ptr(new_entry, head_addr, (ShenandoahStrDedupEntry*)NULL) == NULL) {
  81       Atomic::inc((volatile jint*)&_entries);
  82       return value;
  83     }
  84   }
  85 
  86   ShenandoahStrDedupEntry* volatile head = *head_addr;
  87   assert(head != NULL, "Should not be null");
  88 
  89   while (head != NULL) {
  90     if (head->equals(value, hash)) {
  91       if (new_entry != NULL) release_entry(new_entry);
  92       return head->obj();
  93     } else if (head->next() == NULL) {
  94       if (new_entry == NULL) new_entry = allocate_entry(value, hash);
  95       if (head->cas_set_next(new_entry)) {
  96         Atomic::inc((volatile jint*)&_entries);
  97         return value;
  98       }
  99     }
 100 
 101     count ++;
 102     head = head->next();
 103     assert(head != NULL, "Should not be null");
 104   }
 105 
 106   // Should have found existing one or added new one
 107   ShouldNotReachHere();
 108   return NULL;
 109 }
 110 
 111 void ShenandoahStrDedupTable::add(ShenandoahStrDedupEntry* entry) {
 112   assert(SafepointSynchronize::is_at_safepoint(), "Only at a safepoint");
 113   assert(!use_java_hash(), "Only used when rehashing the table");
 114   unsigned int hash = alt_hash_code(entry->obj());
 115   entry->set_hash(hash);
 116 
 117   ShenandoahStrDedupEntry* volatile* head_addr = bucket_addr(hash_to_index(hash));
 118   if (*head_addr == NULL) {
 119     if (Atomic::cmpxchg_ptr(entry, head_addr, (ShenandoahStrDedupEntry*)NULL) == NULL) {
 120       return;
 121     }
 122   }
 123 
 124   ShenandoahStrDedupEntry* volatile head = *head_addr;
 125   assert(head != NULL, "Should not be null");
 126 
 127   while (head != NULL) {
 128     if (head->next() == NULL && (head->cas_set_next(entry))) {
 129       return;
 130     }
 131 
 132     head = head->next();
 133     // Some one beats us
 134     assert(head != NULL, "Should not be null");
 135   }
 136 }
 137 
 138 bool ShenandoahStrDedupTable::deduplicate(oop java_string) {
 139   assert(java_lang_String::is_instance(java_string), "Must be a string");
 140 
 141   typeArrayOop value = java_lang_String::value(java_string);
 142   if (value == NULL) {
 143     return false;
 144   }
 145 
 146   unsigned int hash = hash_code(java_string, value);
 147 
 148   uintx count = 0;
 149   typeArrayOop existing_value = lookup_or_add(value, hash, count);
 150   assert(existing_value != NULL, "Must have found or added");
 151   if (count > _rehash_threshold) {
 152     _rehash_needed = true;
 153   }
 154 
 155   if (oopDesc::equals(existing_value, value)) {
 156     return false;
 157   }
 158 
 159   // Enqueue the reference to make sure it is kept alive. Concurrent mark might
 160   // otherwise declare it dead if there are no other strong references to this object.
 161   G1SATBCardTableModRefBS::enqueue(existing_value);
 162 
 163   // Existing value found, deduplicate string
 164   java_lang_String::set_value(java_string, typeArrayOop(existing_value));
 165   return true;
 166 }
 167 
 168 
 169 void ShenandoahStrDedupTable::clear_claimed() {
 170   _claimed = 0;
 171   _partition_size = size() / (ShenandoahHeap::heap()->max_workers() * 4);
 172   _partition_size = MAX2(_partition_size, size_t(1));
 173 }
 174 
 175 size_t ShenandoahStrDedupTable::claim() {
 176   return (size_t)Atomic::add((jint)_partition_size, (volatile jint*)&_claimed) - _partition_size;
 177 }
 178 
 179 void ShenandoahStrDedupTable::parallel_oops_do(OopClosure* cl) {
 180   assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
 181 
 182   size_t index;
 183   size_t end_index;
 184   do {
 185     index = claim();
 186     end_index = MIN2(index + partition_size(), size());
 187 
 188     for (; index < end_index; index ++) {
 189       ShenandoahStrDedupEntry* volatile p = bucket(index);
 190       while (p != NULL) {
 191         p->do_oop(cl);
 192         p = p->next();
 193       }
 194     }
 195   } while (index < size());
 196 }
 197 
 198 void ShenandoahStrDedupTable::oops_do_slow(OopClosure* cl) {
 199   assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
 200   for (size_t index = 0; index < size(); index ++) {
 201     ShenandoahStrDedupEntry* volatile p = bucket(index);
 202     while (p != NULL) {
 203       p->do_oop(cl);
 204       p = p->next();
 205     }
 206   }
 207 }
 208 
 209 ShenandoahStrDedupEntry* ShenandoahStrDedupTable::allocate_entry(typeArrayOop value, unsigned int hash) {
 210   ShenandoahStrDedupEntry* entry = new ShenandoahStrDedupEntry();
 211   entry->set_hash(hash);
 212   entry->set_obj(value);
 213   return entry;
 214 }
 215 
 216 void ShenandoahStrDedupTable::release_entry(ShenandoahStrDedupEntry* entry) {
 217   assert(entry != NULL, "null entry");
 218   delete entry;
 219 }
 220 
 221 unsigned int ShenandoahStrDedupTable::hash_code(oop java_string, typeArrayOop value) {
 222   if (use_java_hash()) {
 223     unsigned int hash = java_lang_String::hash(java_string);
 224     if (hash == 0) {
 225       hash = java_hash_code(value);
 226       java_lang_String::set_hash(java_string, hash);
 227     }
 228     return hash;
 229   } else {
 230     return alt_hash_code(value);
 231   }
 232 }
 233 
 234 unsigned int ShenandoahStrDedupTable::java_hash_code(typeArrayOop value) {
 235   assert(use_java_hash(), "Must use java hash code");
 236   int length = value->length();
 237   const jchar* data = (jchar*)value->base(T_CHAR);
 238   return java_lang_String::hash_code(data, length);
 239 }
 240 
 241 unsigned int ShenandoahStrDedupTable::alt_hash_code(typeArrayOop value) {
 242   assert(hash_seed() != 0, "Must have hash seed");
 243   int length = value->length();
 244   const jchar* data = (jchar*)value->base(T_CHAR);
 245   return AltHashing::murmur3_32(hash_seed(), data, length);
 246 }
 247 
 248 void ShenandoahStrDedupTable::print_statistics(outputStream* out) const {
 249   out->print_cr("ShenandoahStrDedupTable: buckets: " SIZE_FORMAT " entries: " SIZE_FORMAT,
 250     size(), _entries);
 251 }
 252 
 253 #ifdef ASSERT
 254 void ShenandoahStrDedupTable::verify() {
 255   assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
 256   assert(Thread::current() == VMThread::vm_thread(), "only by vm thread");
 257   ShenandoahHeap* heap = ShenandoahHeap::heap();
 258 
 259   size_t num_entries = 0;
 260 
 261   for (size_t index = 0; index < size(); index ++) {
 262     ShenandoahStrDedupEntry* volatile head = bucket(index);
 263     while (head != NULL) {
 264       assert(heap->next_marking_context()->is_marked(head->obj()), "Must be marked");
 265 
 266       if (use_java_hash()) {
 267         assert(head->hash() == java_hash_code(head->obj()), "Wrong hash code");
 268       } else {
 269         assert(head->hash() == alt_hash_code(head->obj()), "Wrong alt hash code");
 270       }
 271 
 272       assert(index == hash_to_index(head->hash()), "Wrong bucket");
 273       num_entries ++;
 274       head = head->next();
 275     }
 276   }
 277   assert(num_entries == _entries, "The number of entries does not match");
 278 }
 279 
 280 #endif
 281 
 282 ShenandoahStrDedupTableCleanupTask::ShenandoahStrDedupTableCleanupTask() :
 283         _mark_context(ShenandoahHeap::heap()->next_marking_context()) {
 284 }
 285 
 286 bool ShenandoahStrDedupTableCleanupTask::is_alive(oop obj) const {
 287   return _mark_context->is_marked(obj);
 288 }
 289 
 290 ShenandoahStrDedupTableUnlinkTask::ShenandoahStrDedupTableUnlinkTask(ShenandoahStrDedupTable* const table) :
 291   _table(table) {
 292   log_debug(gc, stringdedup)("Cleanup StringDedup table");
 293   table->clear_claimed();
 294 }
 295 
 296 void ShenandoahStrDedupTableUnlinkTask::do_parallel_cleanup() {
 297   ShenandoahStrDedupTable* const table = _table;
 298   size_t partition = table->partition_size();
 299   size_t removed = 0;
 300   size_t table_end = table->size();
 301 
 302   size_t index;
 303   size_t end_index;
 304   do {
 305     index = table->claim();
 306     end_index = MIN2(index + partition, table_end);
 307     for (; index < end_index; index ++) {
 308       ShenandoahStrDedupEntry* volatile* head_addr = table->bucket_addr(index);
 309       ShenandoahStrDedupEntry* volatile head;
 310       while (*head_addr != NULL) {
 311         head = *head_addr;
 312         if (!is_alive(head->obj())) {
 313           *head_addr = head->next();
 314           table->release_entry(head);
 315           removed ++;
 316         } else {
 317           head_addr = head->next_addr();
 318         }
 319       }
 320     }
 321   } while (index < table_end);
 322 
 323   Atomic::add(-((jlong)removed), (volatile jlong*)&table->_entries);
 324 }
 325 
 326 ShenandoahStrDedupTableRemapTask::ShenandoahStrDedupTableRemapTask(ShenandoahStrDedupTable* const src,
 327                                                                    ShenandoahStrDedupTable* const dest) :
 328   _src_table(src), _dest_table(dest) {
 329   src->clear_claimed();
 330 }
 331 
 332 ShenandoahStrDedupTableRehashTask::ShenandoahStrDedupTableRehashTask(
 333   ShenandoahStrDedupTable* const src, ShenandoahStrDedupTable* const dest) :
 334   ShenandoahStrDedupTableRemapTask(src, dest) {
 335   log_debug(gc, stringdedup)("Rehash StringDedup table");
 336 }
 337 
 338 void ShenandoahStrDedupTableRehashTask::do_parallel_cleanup() {
 339   size_t partition = src_table()->partition_size();
 340 
 341   size_t added = 0;
 342   size_t table_end = src_table()->size();
 343   size_t index;
 344   size_t end_index;
 345   do {
 346     index = src_table()->claim();
 347     end_index = MIN2(index + partition, table_end);
 348     for (; index < end_index; index ++) {
 349       ShenandoahStrDedupEntry* volatile * head_addr = src_table()->bucket_addr(index);
 350       ShenandoahStrDedupEntry* volatile head = *head_addr;
 351       *head_addr = NULL;
 352 
 353       ShenandoahStrDedupEntry* tmp;
 354       while(head != NULL) {
 355         tmp = head;
 356         head = head->next();
 357         tmp->set_next(NULL);
 358         if (is_alive(tmp->obj())) {
 359           dest_table()->add(tmp);
 360           added ++;
 361         } else {
 362           src_table()->release_entry(tmp);
 363         }
 364       }
 365     }
 366   } while (index < table_end);
 367 
 368   Atomic::add((jlong)added, (volatile jlong*)&dest_table()->_entries);
 369 }
 370 
 371 
 372 ShenandoahStrDedupShrinkTableTask::ShenandoahStrDedupShrinkTableTask(
 373   ShenandoahStrDedupTable* const src, ShenandoahStrDedupTable* const dest) :
 374   ShenandoahStrDedupTableRemapTask(src, dest) {
 375   assert(is_power_of_2(src->size()), "Source table size must be a power of 2");
 376   assert(is_power_of_2(dest->size()), "Destination table size must be a power of 2");
 377   assert(src->size() / dest->size() == 2, "Shrink in half");
 378   log_debug(gc, stringdedup)("Shrink StringDedup table");
 379 }
 380 
 381 void ShenandoahStrDedupShrinkTableTask::do_parallel_cleanup() {
 382   size_t partition = src_table()->partition_size();
 383   size_t transferred = 0;
 384 
 385   size_t half_size = src_table()->size() / 2;
 386   // Only scan first half of table.
 387   // To shrink the table in half, we merge buckets at index and (index + half_size)
 388   size_t table_end = src_table()->size() / 2;
 389 
 390   size_t index;
 391   size_t end_index;
 392   do {
 393     index = src_table()->claim();
 394     end_index = MIN2(index + partition, table_end);
 395     for (; index < end_index; index ++) {
 396       ShenandoahStrDedupEntry* volatile * src_head_addr = src_table()->bucket_addr(index);
 397       ShenandoahStrDedupEntry* volatile * dest_head_addr = dest_table()->bucket_addr(index);
 398       ShenandoahStrDedupEntry* volatile   src_head = *src_head_addr;
 399       *src_head_addr = NULL;
 400       // transfer entries at index
 401       transferred += transfer_bucket(src_head, dest_head_addr);
 402 
 403       // transfer entries at index + half_size
 404       src_head_addr = src_table()->bucket_addr(index + half_size);
 405       src_head = *src_head_addr;
 406       *src_head_addr = NULL;
 407       transferred += transfer_bucket(src_head, dest_head_addr);
 408     }
 409   } while (index < table_end);
 410 
 411   Atomic::add((jlong)transferred, (volatile jlong*)&dest_table()->_entries);
 412 }
 413 
 414 
 415 size_t ShenandoahStrDedupShrinkTableTask::transfer_bucket(ShenandoahStrDedupEntry* volatile src,
 416   ShenandoahStrDedupEntry* volatile * dest) {
 417   ShenandoahStrDedupEntry* tmp;
 418   size_t transferred = 0;
 419 
 420   while (src != NULL) {
 421     tmp = src;
 422     src = src->next();
 423     tmp->set_next(NULL);
 424     if (is_alive(tmp->obj())) {
 425       if (*dest != NULL) {
 426         tmp->set_next(*dest);
 427       }
 428       *dest = tmp;
 429       transferred ++;
 430     } else {
 431       src_table()->release_entry(tmp);
 432     }
 433   }
 434 
 435   return transferred;
 436 }
 437 
 438 ShenandoahStrDedupExpandTableTask::ShenandoahStrDedupExpandTableTask(
 439   ShenandoahStrDedupTable* const src, ShenandoahStrDedupTable* const dest) :
 440   ShenandoahStrDedupTableRemapTask(src, dest) {
 441   assert(is_power_of_2(src->size()), "Source table size must be a power of 2");
 442   assert(is_power_of_2(dest->size()), "Destination table size must be a power of 2");
 443   assert(dest->size() == 2 * src->size(), "Double the size");
 444 
 445   log_debug(gc, stringdedup)("Expand StringDedup table");
 446 
 447   int n = exact_log2_long(src->size());
 448   _bit_mask = nth_bit(n);
 449 }
 450 
 451 void ShenandoahStrDedupExpandTableTask::do_parallel_cleanup() {
 452   size_t partition = src_table()->partition_size();
 453   size_t table_end = src_table()->size();
 454 
 455   size_t transferred = 0;
 456   size_t index;
 457   size_t end_index;
 458   do {
 459     index = src_table()->claim();
 460     end_index = MIN2(index + partition, table_end);
 461     for (; index < end_index; index ++) {
 462       // split current source bucket into bucket[index] and bucket[index + half_size]
 463       // in destination table
 464       ShenandoahStrDedupEntry* volatile * src_head_addr = src_table()->bucket_addr(index);
 465       ShenandoahStrDedupEntry* volatile   src_head = *src_head_addr;
 466       ShenandoahStrDedupEntry* volatile * dest_low_addr =  dest_table()->bucket_addr(index);
 467       ShenandoahStrDedupEntry* volatile * dest_high_addr = dest_table()->bucket_addr(index + src_table()->size());
 468       *src_head_addr = NULL;
 469 
 470       transferred += split_bucket(src_head, dest_low_addr, dest_high_addr);
 471     }
 472   } while (index < table_end);
 473   Atomic::add((jlong)transferred, (volatile jlong*)&dest_table()->_entries);
 474 }
 475 
 476 size_t ShenandoahStrDedupExpandTableTask::split_bucket(ShenandoahStrDedupEntry* volatile src,
 477     ShenandoahStrDedupEntry* volatile * dest_low,
 478     ShenandoahStrDedupEntry* volatile * dest_high) {
 479   size_t transferred = 0;
 480 
 481   ShenandoahStrDedupEntry* volatile tmp;
 482   ShenandoahStrDedupEntry* volatile * target;
 483   while (src != NULL) {
 484     tmp = src;
 485     src = src->next();
 486 
 487     if (is_alive(tmp->obj())) {
 488       tmp->set_next(NULL);
 489       unsigned int hash = tmp->hash();
 490       if ((hash & _bit_mask) == 0) {
 491         target = dest_low;
 492       } else {
 493         target = dest_high;
 494       }
 495 
 496       if (*target != NULL) {
 497         tmp->set_next(*target);
 498       }
 499 
 500       *target = tmp;
 501       transferred ++;
 502     } else {
 503       src_table()->release_entry(tmp);
 504     }
 505   }
 506   return transferred;
 507 }