1 /* 2 * Copyright (c) 2015, 2022, Red Hat, Inc. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMARK_INLINE_HPP 26 #define SHARE_GC_SHENANDOAH_SHENANDOAHMARK_INLINE_HPP 27 28 #include "gc/shenandoah/shenandoahMark.hpp" 29 30 #include "gc/shared/continuationGCSupport.inline.hpp" 31 #include "gc/shenandoah/shenandoahAsserts.hpp" 32 #include "gc/shenandoah/shenandoahBarrierSet.inline.hpp" 33 #include "gc/shenandoah/shenandoahClosures.inline.hpp" 34 #include "gc/shenandoah/shenandoahHeap.inline.hpp" 35 #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" 36 #include "gc/shenandoah/shenandoahStringDedup.inline.hpp" 37 #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" 38 #include "gc/shenandoah/shenandoahUtils.hpp" 39 #include "memory/iterator.inline.hpp" 40 #include "oops/compressedOops.inline.hpp" 41 #include "oops/oop.inline.hpp" 42 #include "runtime/prefetch.inline.hpp" 43 #include "utilities/devirtualizer.inline.hpp" 44 #include "utilities/powerOfTwo.hpp" 45 46 template <StringDedupMode STRING_DEDUP> 47 void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) { 48 if (STRING_DEDUP == ENQUEUE_DEDUP) { 49 if (ShenandoahStringDedup::is_candidate(obj)) { 50 req->add(obj); 51 } 52 } else if (STRING_DEDUP == ALWAYS_DEDUP) { 53 if (ShenandoahStringDedup::is_string_candidate(obj) && 54 !ShenandoahStringDedup::dedup_requested(obj)) { 55 req->add(obj); 56 } 57 } 58 } 59 60 template <class T, ShenandoahGenerationType GENERATION, StringDedupMode STRING_DEDUP> 61 void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task) { 62 oop obj = task->obj(); 63 64 shenandoah_assert_not_forwarded(nullptr, obj); 65 shenandoah_assert_marked(nullptr, obj); 66 shenandoah_assert_not_in_cset_except(nullptr, obj, ShenandoahHeap::heap()->cancelled_gc()); 67 68 // Are we in weak subgraph scan? 69 bool weak = task->is_weak(); 70 cl->set_weak(weak); 71 72 if (task->is_not_chunked()) { 73 if (obj->is_instance()) { 74 // Case 1: Normal oop, process as usual. 75 if (ContinuationGCSupport::relativize_stack_chunk(obj)) { 76 // Loom doesn't support mixing of weak marking and strong marking of 77 // stack chunks. 78 cl->set_weak(false); 79 } 80 81 obj->oop_iterate(cl); 82 dedup_string<STRING_DEDUP>(obj, req); 83 } else if (obj->is_objArray()) { 84 // Case 2: Object array instance and no chunk is set. Must be the first 85 // time we visit it, start the chunked processing. 86 do_chunked_array_start<T>(q, cl, obj, weak); 87 } else { 88 // Case 3: Primitive array. Do nothing, no oops there. We use the same 89 // performance tweak TypeArrayKlass::oop_oop_iterate_impl is using: 90 // We skip iterating over the klass pointer since we know that 91 // Universe::TypeArrayKlass never moves. 92 assert (obj->is_typeArray(), "should be type array"); 93 } 94 // Count liveness the last: push the outstanding work to the queues first 95 // Avoid double-counting objects that are visited twice due to upgrade 96 // from final- to strong mark. 97 if (task->count_liveness()) { 98 count_liveness<GENERATION>(live_data, obj); 99 } 100 } else { 101 // Case 4: Array chunk, has sensible chunk id. Process it. 102 do_chunked_array<T>(q, cl, obj, task->chunk(), task->pow(), weak); 103 } 104 } 105 106 template <ShenandoahGenerationType GENERATION> 107 inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop obj) { 108 ShenandoahHeap* const heap = ShenandoahHeap::heap(); 109 size_t region_idx = heap->heap_region_index_containing(obj); 110 ShenandoahHeapRegion* region = heap->get_region(region_idx); 111 size_t size = obj->size(); 112 113 if (!region->is_humongous_start()) { 114 assert(!region->is_humongous(), "Cannot have continuations here"); 115 ShenandoahLiveData cur = live_data[region_idx]; 116 size_t new_val = size + cur; 117 if (new_val >= SHENANDOAH_LIVEDATA_MAX) { 118 // overflow, flush to region data 119 region->increase_live_data_gc_words(new_val); 120 live_data[region_idx] = 0; 121 } else { 122 // still good, remember in locals 123 live_data[region_idx] = (ShenandoahLiveData) new_val; 124 } 125 } else { 126 shenandoah_assert_in_correct_region(nullptr, obj); 127 size_t num_regions = ShenandoahHeapRegion::required_regions(size * HeapWordSize); 128 129 for (size_t i = region_idx; i < region_idx + num_regions; i++) { 130 ShenandoahHeapRegion* chain_reg = heap->get_region(i); 131 assert(chain_reg->is_humongous(), "Expecting a humongous region"); 132 chain_reg->increase_live_data_gc_words(chain_reg->used() >> LogHeapWordSize); 133 } 134 } 135 } 136 137 template <class T> 138 inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop obj, bool weak) { 139 assert(obj->is_objArray(), "expect object array"); 140 objArrayOop array = objArrayOop(obj); 141 int len = array->length(); 142 143 // Mark objArray klass metadata 144 if (Devirtualizer::do_metadata(cl)) { 145 Devirtualizer::do_klass(cl, array->klass()); 146 } 147 148 if (len <= (int) ObjArrayMarkingStride*2) { 149 // A few slices only, process directly 150 array->oop_iterate_range(cl, 0, len); 151 } else { 152 int bits = log2i_graceful(len); 153 // Compensate for non-power-of-two arrays, cover the array in excess: 154 if (len != (1 << bits)) bits++; 155 156 // Only allow full chunks on the queue. This frees do_chunked_array() from checking from/to 157 // boundaries against array->length(), touching the array header on every chunk. 158 // 159 // To do this, we cut the prefix in full-sized chunks, and submit them on the queue. 160 // If the array is not divided in chunk sizes, then there would be an irregular tail, 161 // which we will process separately. 162 163 int last_idx = 0; 164 165 int chunk = 1; 166 int pow = bits; 167 168 // Handle overflow 169 if (pow >= 31) { 170 assert (pow == 31, "sanity"); 171 pow--; 172 chunk = 2; 173 last_idx = (1 << pow); 174 bool pushed = q->push(ShenandoahMarkTask(array, true, weak, 1, pow)); 175 assert(pushed, "overflow queue should always succeed pushing"); 176 } 177 178 // Split out tasks, as suggested in ShenandoahMarkTask docs. Record the last 179 // successful right boundary to figure out the irregular tail. 180 while ((1 << pow) > (int)ObjArrayMarkingStride && 181 (chunk*2 < ShenandoahMarkTask::chunk_size())) { 182 pow--; 183 int left_chunk = chunk*2 - 1; 184 int right_chunk = chunk*2; 185 int left_chunk_end = left_chunk * (1 << pow); 186 if (left_chunk_end < len) { 187 bool pushed = q->push(ShenandoahMarkTask(array, true, weak, left_chunk, pow)); 188 assert(pushed, "overflow queue should always succeed pushing"); 189 chunk = right_chunk; 190 last_idx = left_chunk_end; 191 } else { 192 chunk = left_chunk; 193 } 194 } 195 196 // Process the irregular tail, if present 197 int from = last_idx; 198 if (from < len) { 199 array->oop_iterate_range(cl, from, len); 200 } 201 } 202 } 203 204 template <class T> 205 inline void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop obj, int chunk, int pow, bool weak) { 206 assert(obj->is_objArray(), "expect object array"); 207 objArrayOop array = objArrayOop(obj); 208 209 assert (ObjArrayMarkingStride > 0, "sanity"); 210 211 // Split out tasks, as suggested in ShenandoahMarkTask docs. Avoid pushing tasks that 212 // are known to start beyond the array. 213 while ((1 << pow) > (int)ObjArrayMarkingStride && (chunk*2 < ShenandoahMarkTask::chunk_size())) { 214 pow--; 215 chunk *= 2; 216 bool pushed = q->push(ShenandoahMarkTask(array, true, weak, chunk - 1, pow)); 217 assert(pushed, "overflow queue should always succeed pushing"); 218 } 219 220 int chunk_size = 1 << pow; 221 222 int from = (chunk - 1) * chunk_size; 223 int to = chunk * chunk_size; 224 225 #ifdef ASSERT 226 int len = array->length(); 227 assert (0 <= from && from < len, "from is sane: %d/%d", from, len); 228 assert (0 < to && to <= len, "to is sane: %d/%d", to, len); 229 #endif 230 231 array->oop_iterate_range(cl, from, to); 232 } 233 234 template <ShenandoahGenerationType GENERATION> 235 class ShenandoahSATBBufferClosure : public SATBBufferClosure { 236 private: 237 ShenandoahObjToScanQueue* _queue; 238 ShenandoahHeap* _heap; 239 ShenandoahMarkingContext* const _mark_context; 240 public: 241 ShenandoahSATBBufferClosure(ShenandoahObjToScanQueue* q) : 242 _queue(q), 243 _heap(ShenandoahHeap::heap()), 244 _mark_context(_heap->marking_context()) 245 { 246 } 247 248 void do_buffer(void **buffer, size_t size) { 249 assert(size == 0 || !_heap->has_forwarded_objects(), "Forwarded objects are not expected here"); 250 for (size_t i = 0; i < size; ++i) { 251 oop *p = (oop *) &buffer[i]; 252 ShenandoahMark::mark_through_ref<oop, GENERATION>(p, _queue, _mark_context, false); 253 } 254 } 255 }; 256 257 template<class T, ShenandoahGenerationType GENERATION> 258 inline void ShenandoahMark::mark_through_ref(T* p, ShenandoahObjToScanQueue* q, ShenandoahMarkingContext* const mark_context, bool weak) { 259 T o = RawAccess<>::oop_load(p); 260 if (!CompressedOops::is_null(o)) { 261 oop obj = CompressedOops::decode_not_null(o); 262 263 shenandoah_assert_not_forwarded(p, obj); 264 shenandoah_assert_not_in_cset_except(p, obj, ShenandoahHeap::heap()->cancelled_gc()); 265 266 bool skip_live = false; 267 bool marked; 268 if (weak) { 269 marked = mark_context->mark_weak(obj); 270 } else { 271 marked = mark_context->mark_strong(obj, /* was_upgraded = */ skip_live); 272 } 273 if (marked) { 274 bool pushed = q->push(ShenandoahMarkTask(obj, skip_live, weak)); 275 assert(pushed, "overflow queue should always succeed pushing"); 276 } 277 278 shenandoah_assert_marked(p, obj); 279 } 280 } 281 282 ShenandoahObjToScanQueueSet* ShenandoahMark::task_queues() const { 283 return _task_queues; 284 } 285 286 ShenandoahObjToScanQueue* ShenandoahMark::get_queue(uint index) const { 287 return _task_queues->queue(index); 288 } 289 #endif // SHARE_GC_SHENANDOAH_SHENANDOAHMARK_INLINE_HPP