1 /* 2 * Copyright (c) 2017, 2019, 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 #include "precompiled.hpp" 25 26 #include "classfile/altHashing.hpp" 27 #include "gc_implementation/shenandoah/shenandoahCollectionSet.inline.hpp" 28 #include "gc_implementation/shenandoah/shenandoahHeap.inline.hpp" 29 #include "gc_implementation/shenandoah/shenandoahMarkingContext.inline.hpp" 30 #include "gc_implementation/shenandoah/shenandoahPhaseTimings.hpp" 31 #include "gc_implementation/shenandoah/shenandoahStrDedupQueue.inline.hpp" 32 #include "gc_implementation/shenandoah/shenandoahStrDedupTable.hpp" 33 #include "gc_implementation/shenandoah/shenandoahStrDedupThread.hpp" 34 #include "gc_implementation/shenandoah/shenandoahStringDedup.hpp" 35 #include "gc_implementation/shenandoah/shenandoahWorkGroup.hpp" 36 #include "gc_implementation/shenandoah/shenandoahUtils.hpp" 37 #include "runtime/os.hpp" 38 #include "utilities/workgroup.hpp" 39 40 ShenandoahStrDedupQueueSet* ShenandoahStringDedup::_queues = NULL; 41 ShenandoahStrDedupTable* ShenandoahStringDedup::_table = NULL; 42 ShenandoahStrDedupThread* ShenandoahStringDedup::_thread = NULL; 43 ShenandoahStrDedupStats ShenandoahStringDedup::_stats; 44 bool ShenandoahStringDedup::_enabled = false; 45 46 void ShenandoahStringDedup::initialize() { 47 if (UseStringDeduplication) { 48 _queues = new ShenandoahStrDedupQueueSet(ShenandoahHeap::heap()->max_workers()); 49 _table = new ShenandoahStrDedupTable(); 50 _thread = new ShenandoahStrDedupThread(_queues); 51 _enabled = true; 52 } 53 } 54 55 /* Enqueue candidates for deduplication. 56 * The method should only be called by GC worker threads, during concurrent marking phase. 57 */ 58 void ShenandoahStringDedup::enqueue_candidate(oop java_string, ShenandoahStrDedupQueue* q) { 59 assert(Thread::current()->is_Worker_thread(), "Only be GC worker thread"); 60 61 if (java_string->age() <= StringDeduplicationAgeThreshold) { 62 const markOop mark = java_string->mark(); 63 64 // Having/had displaced header, too risk to deal with them, skip 65 if (mark == markOopDesc::INFLATING() || mark->has_displaced_mark_helper()) { 66 return; 67 } 68 69 // Increase string age and enqueue it when it rearches age threshold 70 markOop new_mark = mark->incr_age(); 71 if (mark == java_string->cas_set_mark(new_mark, mark)) { 72 if (mark->age() == StringDeduplicationAgeThreshold) { 73 q->push(java_string); 74 } 75 } 76 } 77 } 78 79 // Deduplicate a string, return true if it is deduplicated. 80 bool ShenandoahStringDedup::deduplicate(oop java_string, bool update_counter) { 81 assert(is_candidate(java_string), "Not a candidate"); 82 assert(_table != NULL, "Shenandoah Dedup table not initialized"); 83 bool deduped = _table->deduplicate(java_string); 84 85 if (update_counter) { 86 dedup_stats().atomic_inc_inspected(1); 87 if (deduped) { 88 dedup_stats().atomic_inc_skipped(1); 89 } else { 90 dedup_stats().atomic_inc_known(1); 91 } 92 } 93 return deduped; 94 } 95 96 ShenandoahStrDedupQueue* ShenandoahStringDedup::queue(uint worker_id) { 97 assert(_queues != NULL, "QueueSet not initialized"); 98 return _queues->queue_at(worker_id); 99 } 100 101 void ShenandoahStringDedup::threads_do(ThreadClosure* tc) { 102 assert(_thread != NULL, "Shenandoah Dedup Thread not initialized"); 103 tc->do_thread(_thread); 104 } 105 106 void ShenandoahStringDedup::parallel_oops_do(ShenandoahPhaseTimings::Phase phase, OopClosure* cl, uint worker_id) { 107 { 108 ShenandoahWorkerTimingsTracker x(phase, ShenandoahPhaseTimings::StringDedupQueueRoots, worker_id); 109 _queues->parallel_oops_do(cl); 110 } 111 112 { 113 ShenandoahWorkerTimingsTracker x(phase, ShenandoahPhaseTimings::StringDedupTableRoots, worker_id); 114 _table->parallel_oops_do(cl); 115 } 116 117 { 118 ShenandoahWorkerTimingsTracker x(phase, ShenandoahPhaseTimings::StringDedupThreadRoots, worker_id); 119 _thread->parallel_oops_do(cl); 120 } 121 } 122 123 void ShenandoahStringDedup::oops_do_slow(OopClosure* cl) { 124 _queues->oops_do_slow(cl); 125 _table->oops_do_slow(cl); 126 _thread->oops_do_slow(cl); 127 } 128 129 class ShenandoahStrDedupCleanupTask : public AbstractGangTask { 130 private: 131 ShenandoahStrDedupQueueSet* _queues; 132 ShenandoahStrDedupThread* _thread; 133 ShenandoahStrDedupTable** _table; 134 ShenandoahStrDedupTable* _dest_table; 135 136 ShenandoahStrDedupTableCleanupTask* _dedup_table_cleanup_task; 137 138 public: 139 ShenandoahStrDedupCleanupTask(ShenandoahStrDedupQueueSet* qset, 140 ShenandoahStrDedupThread* thread, ShenandoahStrDedupTable** table) 141 : AbstractGangTask("Shenandoah dedup cleanup task"), 142 _queues(qset), _table(table), _thread(thread), _dest_table(NULL) { 143 144 ShenandoahStrDedupTable* the_table = *table; 145 bool rehash = the_table->need_rehash(); 146 size_t table_size = the_table->size(); 147 if (the_table->need_expand()) { 148 table_size *= 2; 149 table_size = MIN2(table_size, ShenandoahStrDedupTable::max_size()); 150 } else if (the_table->need_shrink()) { 151 table_size /= 2; 152 table_size = MAX2(table_size, ShenandoahStrDedupTable::min_size()); 153 } 154 155 if (rehash) { 156 _dest_table = new ShenandoahStrDedupTable(table_size, AltHashing::compute_seed()); 157 _dedup_table_cleanup_task = new ShenandoahStrDedupTableRehashTask(the_table, _dest_table); 158 ShenandoahStringDedup::dedup_stats().inc_table_rehashed(); 159 } else if (the_table->need_expand()) { 160 _dest_table = new ShenandoahStrDedupTable(table_size, the_table->hash_seed()); 161 _dedup_table_cleanup_task = new ShenandoahStrDedupExpandTableTask(the_table, _dest_table); 162 ShenandoahStringDedup::dedup_stats().inc_table_expanded(); 163 } else if (the_table->need_shrink()) { 164 _dest_table = new ShenandoahStrDedupTable(table_size, the_table->hash_seed()); 165 _dedup_table_cleanup_task = new ShenandoahStrDedupShrinkTableTask(the_table, _dest_table); 166 ShenandoahStringDedup::dedup_stats().inc_table_shrinked(); 167 } else { 168 _dedup_table_cleanup_task = new ShenandoahStrDedupTableUnlinkTask(the_table); 169 } 170 } 171 172 ~ShenandoahStrDedupCleanupTask() { 173 assert(_dedup_table_cleanup_task != NULL, "Should not be null"); 174 delete _dedup_table_cleanup_task; 175 176 // Install new table 177 if (_dest_table != NULL) { 178 delete *_table; 179 *_table = _dest_table; 180 } 181 182 (*_table)->verify(); 183 } 184 185 void work(uint worker_id) { 186 _queues->parallel_cleanup(); 187 _thread->parallel_cleanup(); 188 _dedup_table_cleanup_task->do_parallel_cleanup(); 189 } 190 }; 191 192 void ShenandoahStringDedup::parallel_cleanup() { 193 assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); 194 log_debug(gc, stringdedup)("String dedup cleanup"); 195 ShenandoahStringDedup::clear_claimed(); 196 ShenandoahStrDedupCleanupTask task(_queues, _thread, &_table); 197 ShenandoahHeap::heap()->workers()->run_task(&task); 198 } 199 200 void ShenandoahStringDedup::stop() { 201 assert(ShenandoahStringDedup::is_enabled(), "Must be enabled"); 202 assert(_thread != NULL, "Not dedup thread"); 203 _thread->stop(); 204 } 205 206 void ShenandoahStringDedup::clear_claimed() { 207 assert(is_enabled(), "Must be enabled"); 208 _queues->clear_claimed(); 209 _table->clear_claimed(); 210 _thread->clear_claimed(); 211 } 212 213 void ShenandoahStringDedup::print_statistics(outputStream* out) { 214 assert(is_enabled(), "Must be enabled"); 215 216 out->print_cr("Shenandoah String Dedup Statistics:"); 217 dedup_stats().print_statistics(out); 218 _table->print_statistics(out); 219 } 220 221 ShenandoahStrDedupStats::ShenandoahStrDedupStats() : 222 _inspected(0), _deduped(0), _skipped(0), _known(0), _idle(0), _exec(0), _block(0), 223 _idle_elapsed(0), _exec_elapsed(0), _block_elapsed(0), 224 _start_phase(0), _start_concurrent(0), _end_concurrent(0), 225 _table_expanded_count(0), _table_shrinked_count(0), _table_rehashed_count(0) { 226 } 227 228 void ShenandoahStrDedupStats::atomic_inc_inspected(size_t count) { 229 Atomic::add((jlong)count, (volatile jlong*)&_inspected); 230 } 231 232 void ShenandoahStrDedupStats::atomic_inc_skipped(size_t count) { 233 Atomic::add((jlong)count, (volatile jlong*)&_skipped); 234 } 235 236 void ShenandoahStrDedupStats::atomic_inc_deduped(size_t count) { 237 Atomic::add((jlong)count, (volatile jlong*)&_deduped); 238 } 239 240 void ShenandoahStrDedupStats::atomic_inc_known(size_t count) { 241 Atomic::add((jlong)count, (volatile jlong*)&_known); 242 } 243 244 void ShenandoahStrDedupStats::mark_idle() { 245 assert_thread(); 246 _start_phase = os::elapsedTime(); 247 _idle++; 248 } 249 250 void ShenandoahStrDedupStats::mark_exec() { 251 assert_thread(); 252 double now = os::elapsedTime(); 253 _idle_elapsed = now - _start_phase; 254 _start_phase = now; 255 _start_concurrent = now; 256 _exec++; 257 } 258 259 void ShenandoahStrDedupStats::mark_block() { 260 assert_thread(); 261 double now = os::elapsedTime(); 262 _exec_elapsed += now - _start_phase; 263 _start_phase = now; 264 _block++; 265 } 266 267 void ShenandoahStrDedupStats::mark_unblock() { 268 assert_thread(); 269 double now = os::elapsedTime(); 270 _block_elapsed += now - _start_phase; 271 _start_phase = now; 272 } 273 274 void ShenandoahStrDedupStats::mark_done() { 275 assert_thread(); 276 double now = os::elapsedTime(); 277 _exec_elapsed += now - _start_phase; 278 _end_concurrent = now; 279 } 280 281 void ShenandoahStrDedupStats::update(const ShenandoahStrDedupStats& sts) { 282 assert_thread(); 283 // Counters 284 atomic_inc_inspected(sts._inspected); 285 atomic_inc_deduped(sts._deduped); 286 atomic_inc_skipped(sts._skipped); 287 atomic_inc_known(sts._known); 288 289 _idle += sts._idle; 290 _exec += sts._exec; 291 _block += sts._block; 292 293 // Time spent by the deduplication thread in different phases 294 _idle_elapsed += sts._idle_elapsed; 295 _exec_elapsed += sts._exec_elapsed; 296 _block_elapsed += sts._block_elapsed; 297 } 298 299 void ShenandoahStrDedupStats::inc_table_expanded() { 300 assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); 301 assert(Thread::current() == VMThread::vm_thread(), "Only by VM thread"); 302 _table_expanded_count ++; 303 } 304 305 void ShenandoahStrDedupStats::inc_table_shrinked() { 306 assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); 307 assert(Thread::current() == VMThread::vm_thread(), "Only by VM thread"); 308 _table_shrinked_count ++; 309 } 310 311 void ShenandoahStrDedupStats::inc_table_rehashed() { 312 assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); 313 assert(Thread::current() == VMThread::vm_thread(), "Only by VM thread"); 314 _table_rehashed_count ++; 315 } 316 317 void ShenandoahStrDedupStats::print_statistics(outputStream* out) const { 318 out->print_cr(" Inspected: " SIZE_FORMAT_W(12), _inspected); 319 out->print_cr(" Skipped: " SIZE_FORMAT_W(12), _skipped); 320 out->print_cr(" Deduped: " SIZE_FORMAT_W(12), _deduped); 321 out->print_cr(" Known: " SIZE_FORMAT_W(12), _known); 322 out->cr(); 323 out->print_cr(" Idle: " STRDEDUP_TIME_FORMAT_MS " Exec: " STRDEDUP_TIME_FORMAT_MS " Block: " STRDEDUP_TIME_FORMAT_MS, 324 STRDEDUP_TIME_PARAM_MS(_idle_elapsed), STRDEDUP_TIME_PARAM_MS(_exec_elapsed), STRDEDUP_TIME_PARAM_MS(_block_elapsed)); 325 if (_table_expanded_count != 0 || _table_shrinked_count != 0 || _table_rehashed_count != 0) { 326 out->print_cr(" Table expanded: " SIZE_FORMAT " shrinked: " SIZE_FORMAT " rehashed: " SIZE_FORMAT, 327 _table_expanded_count, _table_shrinked_count, _table_rehashed_count); 328 } 329 330 out->cr(); 331 } 332 333 #ifdef ASSERT 334 void ShenandoahStrDedupStats::assert_thread() { 335 assert(Thread::current() == ShenandoahStringDedup::_thread, "Can only be done by dedup thread"); 336 } 337 338 #endif