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/shenandoahHeap.inline.hpp" 34 #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" 35 #include "gc/shenandoah/shenandoahStringDedup.inline.hpp" 36 #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" 37 #include "gc/shenandoah/shenandoahUtils.hpp" 38 #include "memory/iterator.inline.hpp" 39 #include "oops/compressedOops.inline.hpp" 40 #include "oops/oop.inline.hpp" 41 #include "runtime/prefetch.inline.hpp" 42 #include "utilities/devirtualizer.inline.hpp" 43 #include "utilities/powerOfTwo.hpp" 44 45 template <StringDedupMode STRING_DEDUP> 46 void ShenandoahMark::dedup_string(oop obj, StringDedup::Requests* const req) { 47 if (STRING_DEDUP == ENQUEUE_DEDUP) { 48 if (ShenandoahStringDedup::is_candidate(obj)) { 49 req->add(obj); 50 } 51 } else if (STRING_DEDUP == ALWAYS_DEDUP) { 52 if (ShenandoahStringDedup::is_string_candidate(obj) && 53 !ShenandoahStringDedup::dedup_requested(obj)) { 54 req->add(obj); 55 } 56 } 57 } 58 59 template <class T, StringDedupMode STRING_DEDUP> 60 void ShenandoahMark::do_task(ShenandoahObjToScanQueue* q, T* cl, ShenandoahLiveData* live_data, StringDedup::Requests* const req, ShenandoahMarkTask* task) { 61 oop obj = task->obj(); 62 63 // TODO: This will push array chunks into the mark queue with no regard for 64 // generations. I don't think it will break anything, but the young generation 65 // scan might end up processing some old generation array chunks. 66 67 shenandoah_assert_not_forwarded(nullptr, obj); 68 shenandoah_assert_marked(nullptr, obj); 69 shenandoah_assert_not_in_cset_except(nullptr, obj, ShenandoahHeap::heap()->cancelled_gc()); 70 71 // Are we in weak subgraph scan? 72 bool weak = task->is_weak(); 73 cl->set_weak(weak); 74 75 if (task->is_not_chunked()) { 76 if (obj->is_instance()) { 77 // Case 1: Normal oop, process as usual. 78 if (ContinuationGCSupport::relativize_stack_chunk(obj)) { 79 // Loom doesn't support mixing of weak marking and strong marking of 80 // stack chunks. 81 cl->set_weak(false); 82 } 83 84 obj->oop_iterate(cl); 85 dedup_string<STRING_DEDUP>(obj, req); 86 } else if (obj->is_objArray()) { 87 // Case 2: Object array instance and no chunk is set. Must be the first 88 // time we visit it, start the chunked processing. 89 do_chunked_array_start<T>(q, cl, obj, weak); 90 } else { 91 // Case 3: Primitive array. Do nothing, no oops there. We use the same 92 // performance tweak TypeArrayKlass::oop_oop_iterate_impl is using: 93 // We skip iterating over the klass pointer since we know that 94 // Universe::TypeArrayKlass never moves. 95 assert (obj->is_typeArray(), "should be type array"); 96 } 97 // Count liveness the last: push the outstanding work to the queues first 98 // Avoid double-counting objects that are visited twice due to upgrade 99 // from final- to strong mark. 100 if (task->count_liveness()) { 101 count_liveness(live_data, obj); 102 } 103 } else { 104 // Case 4: Array chunk, has sensible chunk id. Process it. 105 do_chunked_array<T>(q, cl, obj, task->chunk(), task->pow(), weak); 106 } 107 } 108 109 inline void ShenandoahMark::count_liveness(ShenandoahLiveData* live_data, oop obj) { 110 ShenandoahHeap* const heap = ShenandoahHeap::heap(); 111 size_t region_idx = heap->heap_region_index_containing(obj); 112 ShenandoahHeapRegion* region = heap->get_region(region_idx); 113 size_t size = obj->size(); 114 115 if (!region->is_humongous_start()) { 116 assert(!region->is_humongous(), "Cannot have continuations here"); 117 assert(region->affiliation() != FREE, "Do not count live data within Free Regular Region " SIZE_FORMAT, region_idx); 118 ShenandoahLiveData cur = live_data[region_idx]; 119 size_t new_val = size + cur; 120 if (new_val >= SHENANDOAH_LIVEDATA_MAX) { 121 // overflow, flush to region data 122 region->increase_live_data_gc_words(new_val); 123 live_data[region_idx] = 0; 124 } else { 125 // still good, remember in locals 126 live_data[region_idx] = (ShenandoahLiveData) new_val; 127 } 128 } else { 129 shenandoah_assert_in_correct_region(nullptr, obj); 130 size_t num_regions = ShenandoahHeapRegion::required_regions(size * HeapWordSize); 131 132 assert(region->affiliation() != FREE, "Do not count live data within FREE Humongous Start Region " SIZE_FORMAT, region_idx); 133 for (size_t i = region_idx; i < region_idx + num_regions; i++) { 134 ShenandoahHeapRegion* chain_reg = heap->get_region(i); 135 assert(chain_reg->is_humongous(), "Expecting a humongous region"); 136 assert(chain_reg->affiliation() != FREE, "Do not count live data within FREE Humongous Continuation Region " SIZE_FORMAT, i); 137 chain_reg->increase_live_data_gc_words(chain_reg->used() >> LogHeapWordSize); 138 } 139 } 140 } 141 142 template <class T> 143 inline void ShenandoahMark::do_chunked_array_start(ShenandoahObjToScanQueue* q, T* cl, oop obj, bool weak) { 144 assert(obj->is_objArray(), "expect object array"); 145 objArrayOop array = objArrayOop(obj); 146 int len = array->length(); 147 148 // Mark objArray klass metadata 149 if (Devirtualizer::do_metadata(cl)) { 150 Devirtualizer::do_klass(cl, array->klass()); 151 } 152 153 if (len <= (int) ObjArrayMarkingStride*2) { 154 // A few slices only, process directly 155 array->oop_iterate_range(cl, 0, len); 156 } else { 157 int bits = log2i_graceful(len); 158 // Compensate for non-power-of-two arrays, cover the array in excess: 159 if (len != (1 << bits)) bits++; 160 161 // Only allow full chunks on the queue. This frees do_chunked_array() from checking from/to 162 // boundaries against array->length(), touching the array header on every chunk. 163 // 164 // To do this, we cut the prefix in full-sized chunks, and submit them on the queue. 165 // If the array is not divided in chunk sizes, then there would be an irregular tail, 166 // which we will process separately. 167 168 int last_idx = 0; 169 170 int chunk = 1; 171 int pow = bits; 172 173 // Handle overflow 174 if (pow >= 31) { 175 assert (pow == 31, "sanity"); 176 pow--; 177 chunk = 2; 178 last_idx = (1 << pow); 179 bool pushed = q->push(ShenandoahMarkTask(array, true, weak, 1, pow)); 180 assert(pushed, "overflow queue should always succeed pushing"); 181 } 182 183 // Split out tasks, as suggested in ShenandoahMarkTask docs. Record the last 184 // successful right boundary to figure out the irregular tail. 185 while ((1 << pow) > (int)ObjArrayMarkingStride && 186 (chunk*2 < ShenandoahMarkTask::chunk_size())) { 187 pow--; 188 int left_chunk = chunk*2 - 1; 189 int right_chunk = chunk*2; 190 int left_chunk_end = left_chunk * (1 << pow); 191 if (left_chunk_end < len) { 192 bool pushed = q->push(ShenandoahMarkTask(array, true, weak, left_chunk, pow)); 193 assert(pushed, "overflow queue should always succeed pushing"); 194 chunk = right_chunk; 195 last_idx = left_chunk_end; 196 } else { 197 chunk = left_chunk; 198 } 199 } 200 201 // Process the irregular tail, if present 202 int from = last_idx; 203 if (from < len) { 204 array->oop_iterate_range(cl, from, len); 205 } 206 } 207 } 208 209 template <class T> 210 inline void ShenandoahMark::do_chunked_array(ShenandoahObjToScanQueue* q, T* cl, oop obj, int chunk, int pow, bool weak) { 211 assert(obj->is_objArray(), "expect object array"); 212 objArrayOop array = objArrayOop(obj); 213 214 assert (ObjArrayMarkingStride > 0, "sanity"); 215 216 // Split out tasks, as suggested in ShenandoahMarkTask docs. Avoid pushing tasks that 217 // are known to start beyond the array. 218 while ((1 << pow) > (int)ObjArrayMarkingStride && (chunk*2 < ShenandoahMarkTask::chunk_size())) { 219 pow--; 220 chunk *= 2; 221 bool pushed = q->push(ShenandoahMarkTask(array, true, weak, chunk - 1, pow)); 222 assert(pushed, "overflow queue should always succeed pushing"); 223 } 224 225 int chunk_size = 1 << pow; 226 227 int from = (chunk - 1) * chunk_size; 228 int to = chunk * chunk_size; 229 230 #ifdef ASSERT 231 int len = array->length(); 232 assert (0 <= from && from < len, "from is sane: %d/%d", from, len); 233 assert (0 < to && to <= len, "to is sane: %d/%d", to, len); 234 #endif 235 236 array->oop_iterate_range(cl, from, to); 237 } 238 239 template <GenerationMode GENERATION> 240 class ShenandoahSATBBufferClosure : public SATBBufferClosure { 241 private: 242 ShenandoahObjToScanQueue* _queue; 243 ShenandoahObjToScanQueue* _old; 244 ShenandoahHeap* _heap; 245 ShenandoahMarkingContext* const _mark_context; 246 public: 247 ShenandoahSATBBufferClosure(ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old) : 248 _queue(q), 249 _old(old), 250 _heap(ShenandoahHeap::heap()), 251 _mark_context(_heap->marking_context()) 252 { 253 } 254 255 void do_buffer(void **buffer, size_t size) { 256 assert(size == 0 || !_heap->has_forwarded_objects() || _heap->is_concurrent_old_mark_in_progress(), "Forwarded objects are not expected here"); 257 for (size_t i = 0; i < size; ++i) { 258 oop *p = (oop *) &buffer[i]; 259 ShenandoahMark::mark_through_ref<oop, GENERATION>(p, _queue, _old, _mark_context, false); 260 } 261 } 262 }; 263 264 template<GenerationMode GENERATION> 265 bool ShenandoahMark::in_generation(oop obj) { 266 // Each in-line expansion of in_generation() resolves GENERATION at compile time. 267 if (GENERATION == YOUNG) 268 return ShenandoahHeap::heap()->is_in_young(obj); 269 else if (GENERATION == OLD) 270 return ShenandoahHeap::heap()->is_in_old(obj); 271 else if (GENERATION == GLOBAL) 272 return true; 273 else 274 return false; 275 } 276 277 template<class T, GenerationMode GENERATION> 278 inline void ShenandoahMark::mark_through_ref(T *p, ShenandoahObjToScanQueue* q, ShenandoahObjToScanQueue* old, ShenandoahMarkingContext* const mark_context, bool weak) { 279 T o = RawAccess<>::oop_load(p); 280 if (!CompressedOops::is_null(o)) { 281 oop obj = CompressedOops::decode_not_null(o); 282 283 ShenandoahHeap* heap = ShenandoahHeap::heap(); 284 shenandoah_assert_not_forwarded(p, obj); 285 shenandoah_assert_not_in_cset_except(p, obj, heap->cancelled_gc()); 286 if (in_generation<GENERATION>(obj)) { 287 mark_ref(q, mark_context, weak, obj); 288 shenandoah_assert_marked(p, obj); 289 if (heap->mode()->is_generational()) { 290 // TODO: As implemented herein, GLOBAL collections reconstruct the card table during GLOBAL concurrent 291 // marking. Note that the card table is cleaned at init_mark time so it needs to be reconstructed to support 292 // future young-gen collections. It might be better to reconstruct card table in 293 // ShenandoahHeapRegion::global_oop_iterate_and_fill_dead. We could either mark all live memory as dirty, or could 294 // use the GLOBAL update-refs scanning of pointers to determine precisely which cards to flag as dirty. 295 if (GENERATION == YOUNG && heap->is_in_old(p)) { 296 // Mark card as dirty because remembered set scanning still finds interesting pointer. 297 heap->mark_card_as_dirty((HeapWord*)p); 298 } else if (GENERATION == GLOBAL && heap->is_in_old(p) && heap->is_in_young(obj)) { 299 // Mark card as dirty because GLOBAL marking finds interesting pointer. 300 heap->mark_card_as_dirty((HeapWord*)p); 301 } 302 } 303 } else if (old != nullptr) { 304 // Young mark, bootstrapping old or concurrent with old marking. 305 mark_ref(old, mark_context, weak, obj); 306 shenandoah_assert_marked(p, obj); 307 } else if (GENERATION == OLD) { 308 // Old mark, found a young pointer. 309 // TODO: Rethink this: may be redundant with dirtying of cards identified during young-gen remembered set scanning 310 // and by mutator write barriers. Assert 311 if (heap->is_in(p)) { 312 assert(heap->is_in_young(obj), "Expected young object."); 313 heap->mark_card_as_dirty(p); 314 } 315 } 316 } 317 } 318 319 inline void ShenandoahMark::mark_ref(ShenandoahObjToScanQueue* q, 320 ShenandoahMarkingContext* const mark_context, 321 bool weak, oop obj) { 322 bool skip_live = false; 323 bool marked; 324 if (weak) { 325 marked = mark_context->mark_weak(obj); 326 } else { 327 marked = mark_context->mark_strong(obj, /* was_upgraded = */ skip_live); 328 } 329 if (marked) { 330 bool pushed = q->push(ShenandoahMarkTask(obj, skip_live, weak)); 331 assert(pushed, "overflow queue should always succeed pushing"); 332 } 333 } 334 335 ShenandoahObjToScanQueueSet* ShenandoahMark::task_queues() const { 336 return _task_queues; 337 } 338 339 ShenandoahObjToScanQueue* ShenandoahMark::get_queue(uint index) const { 340 return _task_queues->queue(index); 341 } 342 343 ShenandoahObjToScanQueue* ShenandoahMark::get_old_queue(uint index) const { 344 if (_old_gen_task_queues != nullptr) { 345 return _old_gen_task_queues->queue(index); 346 } 347 return nullptr; 348 } 349 350 #endif // SHARE_GC_SHENANDOAH_SHENANDOAHMARK_INLINE_HPP