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 #include "precompiled.hpp" 25 26 #include "gc_implementation/shared/suspendibleThreadSet.hpp" 27 #include "gc_implementation/shenandoah/shenandoahHeap.inline.hpp" 28 #include "gc_implementation/shenandoah/shenandoahLogging.hpp" 29 #include "gc_implementation/shenandoah/shenandoahStrDedupQueue.inline.hpp" 30 #include "gc_implementation/shenandoah/shenandoahStrDedupThread.hpp" 31 #include "gc_implementation/shenandoah/shenandoahStringDedup.hpp" 32 #include "gc_implementation/shenandoah/shenandoahUtils.hpp" 33 34 ShenandoahStrDedupThread::ShenandoahStrDedupThread(ShenandoahStrDedupQueueSet* queues) : 35 ConcurrentGCThread(), _queues(queues), _claimed(0) { 36 size_t num_queues = queues->num_queues(); 37 _work_list = NEW_C_HEAP_ARRAY(QueueChunkedList*, num_queues, mtGC); 38 for (size_t index = 0; index < num_queues; index ++) { 39 _work_list[index] = NULL; 40 } 41 42 set_name("%s", "ShenandoahStringDedupTherad"); 43 create_and_start(); 44 } 45 46 ShenandoahStrDedupThread::~ShenandoahStrDedupThread() { 47 ShouldNotReachHere(); 48 } 49 50 void ShenandoahStrDedupThread::run() { 51 initialize_in_thread(); 52 wait_for_universe_init(); 53 54 for (;;) { 55 ShenandoahStrDedupStats stats; 56 57 assert(is_work_list_empty(), "Work list must be empty"); 58 // Queue has been shutdown 59 if (!poll(&stats)) { 60 assert(queues()->has_terminated(), "Must be terminated"); 61 break; 62 } 63 64 // Include thread in safepoints 65 SuspendibleThreadSetJoiner sts_join; 66 // Process the queue 67 for (uint queue_index = 0; queue_index < queues()->num_queues(); queue_index ++) { 68 QueueChunkedList* cur_list = _work_list[queue_index]; 69 70 while (cur_list != NULL) { 71 stats.mark_exec(); 72 73 while (!cur_list->is_empty()) { 74 oop java_string = cur_list->pop(); 75 stats.inc_inspected(); 76 assert(!ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Must not at Shenandoah safepoint"); 77 78 if (oopDesc::is_null(java_string) || 79 !ShenandoahStringDedup::is_candidate(java_string)) { 80 stats.inc_skipped(); 81 } else { 82 if (ShenandoahStringDedup::deduplicate(java_string, false /* update counter */)) { 83 stats.inc_deduped(); 84 } else { 85 stats.inc_known(); 86 } 87 } 88 89 // Safepoint this thread if needed 90 if (sts_join.should_yield()) { 91 stats.mark_block(); 92 sts_join.yield(); 93 stats.mark_unblock(); 94 } 95 } 96 97 // Advance list only after processed. Otherwise, we may miss scanning 98 // during safepoints 99 _work_list[queue_index] = cur_list->next(); 100 queues()->release_chunked_list(cur_list); 101 cur_list = _work_list[queue_index]; 102 } 103 } 104 105 stats.mark_done(); 106 107 ShenandoahStringDedup::dedup_stats().update(stats); 108 109 if (ShenandoahLogDebug) { 110 stats.print_statistics(tty); 111 } 112 } 113 114 if (ShenandoahLogDebug) { 115 ShenandoahStringDedup::print_statistics(tty); 116 } 117 } 118 119 void ShenandoahStrDedupThread::stop() { 120 queues()->terminate(); 121 } 122 123 void ShenandoahStrDedupThread::parallel_oops_do(OopClosure* cl) { 124 assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); 125 size_t claimed_index; 126 while ((claimed_index = claim()) < queues()->num_queues()) { 127 QueueChunkedList* q = _work_list[claimed_index]; 128 while (q != NULL) { 129 q->oops_do(cl); 130 q = q->next(); 131 } 132 } 133 } 134 135 void ShenandoahStrDedupThread::oops_do_slow(OopClosure* cl) { 136 assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint"); 137 for (size_t index = 0; index < queues()->num_queues(); index ++) { 138 QueueChunkedList* q = _work_list[index]; 139 while (q != NULL) { 140 q->oops_do(cl); 141 q = q->next(); 142 } 143 } 144 } 145 146 bool ShenandoahStrDedupThread::is_work_list_empty() const { 147 assert(Thread::current() == this, "Only from dedup thread"); 148 for (uint index = 0; index < queues()->num_queues(); index ++) { 149 if (_work_list[index] != NULL) return false; 150 } 151 return true; 152 } 153 154 void ShenandoahStrDedupThread::parallel_cleanup() { 155 ShenandoahStrDedupQueueCleanupClosure cl; 156 parallel_oops_do(&cl); 157 } 158 159 bool ShenandoahStrDedupThread::poll(ShenandoahStrDedupStats* stats) { 160 assert(is_work_list_empty(), "Only poll when work list is empty"); 161 162 while (!_queues->has_terminated()) { 163 { 164 bool has_work = false; 165 stats->mark_exec(); 166 // Include thread in safepoints 167 SuspendibleThreadSetJoiner sts_join; 168 169 for (uint index = 0; index < queues()->num_queues(); index ++) { 170 assert(!ShenandoahSafepoint::is_at_shenandoah_safepoint(), "Not at Shenandoah Safepoint"); 171 _work_list[index] = queues()->remove_work_list_atomic(index); 172 if (_work_list[index] != NULL) { 173 has_work = true; 174 } 175 176 // Safepoint this thread if needed 177 if (sts_join.should_yield()) { 178 stats->mark_block(); 179 sts_join.yield(); 180 stats->mark_unblock(); 181 } 182 } 183 184 if (has_work) return true; 185 } 186 187 { 188 stats->mark_idle(); 189 MonitorLockerEx locker(queues()->lock(), Monitor::_no_safepoint_check_flag); 190 locker.wait(Mutex::_no_safepoint_check_flag); 191 } 192 } 193 return false; 194 } 195 196 size_t ShenandoahStrDedupThread::claim() { 197 size_t index = (size_t)Atomic::add(1, (volatile jint*)&_claimed) - 1; 198 return index; 199 }