• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "runtime/mem/gc/g1/g1-gc.h"
17 
18 #include "runtime/include/panda_vm.h"
19 #include "runtime/mem/gc/card_table-inl.h"
20 #include "runtime/mem/gc/dynamic/gc_marker_dynamic-inl.h"
21 #include "runtime/mem/gc/g1/ref_cache_builder.h"
22 #include "runtime/mem/gc/g1/update_remset_thread.h"
23 #include "runtime/mem/gc/gc_workers_thread_pool.h"
24 #include "runtime/mem/gc/generational-gc-base-inl.h"
25 #include "runtime/mem/gc/static/gc_marker_static-inl.h"
26 #include "runtime/mem/gc/reference-processor/reference_processor.h"
27 #include "runtime/mem/object_helpers-inl.h"
28 #include "runtime/mem/refstorage/global_object_storage.h"
29 #include "runtime/mem/rem_set-inl.h"
30 #include "runtime/include/thread.h"
31 #include "runtime/include/managed_thread.h"
32 
33 namespace panda::mem {
34 
ToObjPtr(const void * ptr)35 static inline object_pointer_type ToObjPtr(const void *ptr)
36 {
37     return static_cast<object_pointer_type>(ToUintPtr(ptr));
38 }
39 
GetG1BarrierSet()40 static inline GCG1BarrierSet *GetG1BarrierSet()
41 {
42     Thread *thread = Thread::GetCurrent();
43     ASSERT(thread != nullptr);
44     GCBarrierSet *barrier_set = thread->GetBarrierSet();
45     ASSERT(barrier_set != nullptr);
46     return static_cast<GCG1BarrierSet *>(barrier_set);
47 }
48 
PreWrbFuncEntrypoint(void * old_value)49 extern "C" void PreWrbFuncEntrypoint(void *old_value)
50 {
51     // The cast below is needed to truncate high 32bits from 64bit pointer
52     // in case object pointers have 32bit.
53     old_value = ToVoidPtr(ToObjPtr(old_value));
54     LOG(DEBUG, GC) << "G1GC pre barrier val = " << std::hex << old_value;
55     auto *thread = ManagedThread::GetCurrent();
56     // thread can't be null here because pre-barrier is called only in concurrent-mark, but we don't process
57     // weak-references in concurrent mark
58     ASSERT(thread != nullptr);
59     auto buffer_vec = thread->GetPreBuff();
60     buffer_vec->push_back(static_cast<ObjectHeader *>(old_value));
61 }
62 
63 /*
64  * TSAN doesn't understand that we hold a lock already when we call it from OnThreadTerminate with need_lock=false and
65  * requires to defend by lock
66  */
67 template <bool need_lock = true>
PushCardToUpdateThread(mem::GCG1BarrierSet::G1PostBarrierRingBufferType * cards,mem::GCG1BarrierSet * barrier_set)68 static NO_THREAD_SAFETY_ANALYSIS void PushCardToUpdateThread(mem::GCG1BarrierSet::G1PostBarrierRingBufferType *cards,
69                                                              mem::GCG1BarrierSet *barrier_set)
70 {
71     ASSERT(cards != nullptr);
72     ASSERT(barrier_set != nullptr);
73     LOG(DEBUG, GC) << "Call PushCardToUpdateThread with vector: " << cards;
74     while (true) {
75         mem::CardTable::CardPtr card;
76         bool has_element = cards->TryPop(&card);
77         if (!has_element) {
78             break;
79         }
80         ASSERT(barrier_set != nullptr);
81         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
82         if constexpr (need_lock) {
83             barrier_set->GetQueueLock()->Lock();
84         }
85         barrier_set->GetUpdatedRefsQueue()->push_back(card);
86         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
87         if constexpr (need_lock) {
88             barrier_set->GetQueueLock()->Unlock();
89         }
90     }
91 }
92 
93 // PostWrbUpdateCardFuncEntrypoint
PostWrbUpdateCardFuncEntrypoint(const void * from,const void * to)94 extern "C" void PostWrbUpdateCardFuncEntrypoint(const void *from, const void *to)
95 {
96     ASSERT(from != nullptr);
97     ASSERT(to != nullptr);
98     // The cast below is needed to truncate high 32bits from 64bit pointer
99     // in case object pointers have 32bit.
100     from = ToVoidPtr(ToObjPtr(from));
101     GCG1BarrierSet *barriers = GetG1BarrierSet();
102     ASSERT(barriers != nullptr);
103     auto card_table = barriers->GetCardTable();
104     ASSERT(card_table != nullptr);
105     // No need to keep remsets for young->young
106     // TODO(dtrubenkov): add assert that we do not have young -> young reference here
107     auto card = card_table->GetCardPtr(ToUintPtr(from));
108     LOG(DEBUG, GC) << "G1GC post queue add ref: " << std::hex << from << " -> " << ToVoidPtr(ToObjPtr(to))
109                    << " from_card: " << card;
110     // TODO(dtrubenkov): remove !card->IsYoung() after it will be encoded in compiler barrier
111     if ((card->IsClear()) && (!card->IsYoung())) {
112         // TODO(dtrubenkov): either encode this in compiler barrier or remove from Interpreter barrier (if move to
113         // INT/JIT parts then don't check IsClear here cause it will be marked already)
114         card->Mark();
115 
116         auto thread = ManagedThread::GetCurrent();
117         if (thread == nullptr) {  // slow path via shared-queue for VM threads: gc/compiler/etc
118             os::memory::LockHolder lock(*barriers->GetQueueLock());
119             barriers->GetUpdatedRefsQueue()->push_back(card);
120         } else {
121             // general fast-path for mutators
122             ASSERT(thread->GetPreBuff() != nullptr);  // write barrier cant be called after Terminate
123             auto buffer = thread->GetG1PostBarrierBuffer();
124             ASSERT(buffer != nullptr);
125             buffer->Push(card);
126         }
127     }
128 }
129 
130 template <class LanguageConfig>
G1GC(ObjectAllocatorBase * object_allocator,const GCSettings & settings)131 G1GC<LanguageConfig>::G1GC(ObjectAllocatorBase *object_allocator, const GCSettings &settings)
132     : GenerationalGC<LanguageConfig>(object_allocator, settings),
133       marker_(this),
134       concurrent_marking_stack_(this),
135       region_garbage_rate_threshold_(settings.G1RegionGarbageRateThreshold()),
136       g1_promotion_region_alive_rate_(settings.G1PromotionRegionAliveRate()),
137       g1_track_freed_objects_(settings.G1TrackFreedObjects())
138 {
139     InternalAllocatorPtr allocator = this->GetInternalAllocator();
140     this->SetType(GCType::G1_GC);
141     this->SetTLABsSupported();
142     updated_refs_queue_ = allocator->New<GCG1BarrierSet::ThreadLocalCardQueues>();
143     calc_live_bytes_ = [this](const ObjectHeader *obj) {
144         Region *region = ObjectToRegion(obj);
145         if (!this->marker_.IsMarked(obj) && region->HasFlag(IS_OLD)) {
146             size_t object_size = GetAlignedObjectSize(GetObjectSize(obj));
147             region->AddLiveBytesConcurrently(object_size);
148         }
149         return true;
150     };
151 
152     os::memory::LockHolder lock(unassigned_buffers_lock_);
153     size_t workers_count = this->GetSettings()->GCWorkersCount() + 1;
154     unique_refs_from_remsets_.resize(workers_count);
155     unassigned_buffers_.reserve(workers_count);
156     for (auto &buf : unique_refs_from_remsets_) {
157         buf.reserve(MAX_REFS / workers_count);
158         unassigned_buffers_.push_back(&buf);
159     }
160 }
161 
162 template <class LanguageConfig>
~G1GC()163 G1GC<LanguageConfig>::~G1GC()
164 {
165     InternalAllocatorPtr allocator = this->GetInternalAllocator();
166     {
167         for (auto obj_vector : satb_buff_list_) {
168             allocator->Delete(obj_vector);
169         }
170     }
171     allocator->Delete(updated_refs_queue_);
172     this->GetInternalAllocator()->Delete(update_remset_thread_);
173 }
174 
175 template <class LanguageConfig>
WaitForGC(GCTask task)176 void G1GC<LanguageConfig>::WaitForGC(GCTask task)
177 {
178     GenerationalGC<LanguageConfig>::WaitForGC(task);
179 }
180 
181 template <class LanguageConfig>
InitGCBits(panda::ObjectHeader * obj_header)182 void G1GC<LanguageConfig>::InitGCBits(panda::ObjectHeader *obj_header)
183 {
184     // The mutator may create a new object during concurrent marking phase.
185     // In this case GC may don't mark it (for example only vregs may contain reference to the new object)
186     // and collect. To avoid such situations add objects to a special buffer which
187     // will be processed at remark stage.
188     if (this->GetCardTable()->GetCardPtr(ToUintPtr(obj_header))->IsYoung() || !concurrent_marking_flag_) {
189         return;
190     }
191     os::memory::LockHolder lock(satb_and_newobj_buf_lock_);
192     newobj_buffer_.push_back(obj_header);
193 }
194 
195 template <class LanguageConfig>
PreStartupImp()196 void G1GC<LanguageConfig>::PreStartupImp()
197 {
198     GenerationalGC<LanguageConfig>::DisableTenuredGC();
199 }
200 
201 template <class LanguageConfig>
Trigger()202 void G1GC<LanguageConfig>::Trigger()
203 {
204     auto task = MakePandaUnique<GCTask>(GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE, time::GetCurrentTimeInNanos());
205     this->AddGCTask(true, std::move(task), true);
206 }
207 
208 template <class LanguageConfig>
209 template <RegionFlag region_type>
DoRegionCompacting(Region * region,bool use_gc_workers,PandaVector<PandaVector<ObjectHeader * > * > * moved_objects_vector)210 void G1GC<LanguageConfig>::DoRegionCompacting(Region *region, bool use_gc_workers,
211                                               PandaVector<PandaVector<ObjectHeader *> *> *moved_objects_vector)
212 {
213     auto internal_allocator = this->GetInternalAllocator();
214     if (use_gc_workers) {
215         auto vector = internal_allocator->template New<PandaVector<ObjectHeader *>>();
216         moved_objects_vector->push_back(vector);
217         auto storage = internal_allocator->template New<GCWorkersTask::RegionDataType>(vector, region);
218         if (!this->GetWorkersPool()->AddTask(GCWorkersTaskTypes::TASK_REGION_COMPACTING, storage)) {
219             // We couldn't send a task to workers. Therefore, do it here.
220             internal_allocator->Delete(storage);
221             RegionCompactingImpl<true, region_type>(vector, region);
222         }
223     } else {
224         ASSERT(moved_objects_vector->size() == 1);
225         RegionCompactingImpl<false, region_type>(moved_objects_vector->back(), region);
226     }
227 }
228 
229 template <class LanguageConfig>
230 template <bool atomic>
RegionPromotionImpl(PandaVector<ObjectHeader * > * moved_objects,Region * region)231 void G1GC<LanguageConfig>::RegionPromotionImpl(PandaVector<ObjectHeader *> *moved_objects, Region *region)
232 {
233     size_t move_size = 0;
234     size_t move_count = 0;
235     auto object_allocator = this->GetG1ObjectAllocator();
236     auto promotion_move_checker = [&moved_objects](ObjectHeader *src) {
237         LOG_DEBUG_OBJECT_EVENTS << "PROMOTE YOUNG object " << src;
238         ASSERT(ObjectToRegion(src)->HasFlag(RegionFlag::IS_EDEN));
239         moved_objects->push_back(src);
240     };
241     auto promotion_death_checker = [this, &move_count, &move_size](ObjectHeader *object_header) {
242         ++move_count;
243         move_size += GetAlignedObjectSize(object_header->ObjectSize());
244         if (IsMarked(object_header)) {
245             return ObjectStatus::ALIVE_OBJECT;
246         }
247         LOG_DEBUG_OBJECT_EVENTS << "PROMOTE DEAD YOUNG object " << object_header;
248         return ObjectStatus::DEAD_OBJECT;
249     };
250     object_allocator->PromoteYoungRegion(region, promotion_death_checker, promotion_move_checker);
251     region->RmvFlag(RegionFlag::IS_COLLECTION_SET);
252     this->mem_stats_.template RecordSizeMovedYoung<atomic>(move_size);
253     this->mem_stats_.template RecordCountMovedYoung<atomic>(move_count);
254 }
255 
256 template <class LanguageConfig>
CollectRefsFromCard(CardTable::CardPtr card,Region * region,RefVector * refs_from_remsets)257 void G1GC<LanguageConfig>::CollectRefsFromCard(CardTable::CardPtr card, Region *region, RefVector *refs_from_remsets)
258 {
259     auto mem_range = this->GetCardTable()->GetMemoryRange(card);
260     auto visitor = [this, refs_from_remsets](void *mem) {
261         auto obj = static_cast<ObjectHeader *>(mem);
262         RefCacheBuilder<LanguageConfig> builder(this, refs_from_remsets, &unique_objects_from_remsets_,
263                                                 &objects_from_remsets_lock_);
264         ObjectHelpers<LanguageConfig::LANG_TYPE>::TraverseAllObjectsWithInfo(obj, builder);
265     };
266     region->GetLiveBitmap()->IterateOverMarkedChunkInRange(ToVoidPtr(mem_range.GetStartAddress()),
267                                                            ToVoidPtr(mem_range.GetEndAddress()), visitor);
268 }
269 
270 template <class LanguageConfig>
271 template <bool atomic, bool concurrently>
CollectNonRegularObjects(GCTask & task)272 void G1GC<LanguageConfig>::CollectNonRegularObjects(GCTask &task)
273 {
274     size_t delete_size = 0;
275     size_t delete_count = 0;
276     auto death_checker = [&delete_size, &delete_count](ObjectHeader *object_header) {
277         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
278         if constexpr (concurrently) {
279             // We may face a newly created object without live bitmap initialization.
280             if (object_header->template AtomicClassAddr<BaseClass>() == nullptr) {
281                 return ObjectStatus::ALIVE_OBJECT;
282             }
283         }
284         Region *region = ObjectToRegion(object_header);
285         auto live_bitmap = region->GetLiveBitmap();
286         if (live_bitmap->AtomicTest(object_header)) {
287             return ObjectStatus::ALIVE_OBJECT;
288         }
289 
290         if (region->HasFlag(RegionFlag::IS_LARGE_OBJECT)) {
291             LOG_DEBUG_OBJECT_EVENTS << "DELETE HUMONGOUS object " << object_header;
292             // humongous allocator increases size by region size
293             delete_size += region->Size();
294             ++delete_count;
295         } else {
296             ASSERT(region->HasFlag(RegionFlag::IS_NONMOVABLE));
297             LOG_DEBUG_OBJECT_EVENTS << "DELETE NON MOVABLE object " << object_header;
298         }
299         return ObjectStatus::DEAD_OBJECT;
300     };
301     auto region_visitor = [this](PandaVector<Region *> &regions) {
302         update_remset_thread_->InvalidateRegions(&regions);
303     };
304     this->GetG1ObjectAllocator()->CollectNonRegularRegions(region_visitor, death_checker);
305     this->mem_stats_.template RecordCountFreedTenured<atomic>(delete_count);
306     this->mem_stats_.template RecordSizeFreedTenured<atomic>(delete_size);
307     task.UpdateGCCollectionType(GCCollectionType::TENURED);
308 }
309 
310 template <class LanguageConfig>
NeedToPromote(const Region * region) const311 bool G1GC<LanguageConfig>::NeedToPromote(const Region *region) const
312 {
313     ASSERT(region->HasFlag(RegionFlag::IS_EDEN));
314     // Issue 8183: Remove unnessesary IsYoungFullGC check after refactoring Full GC
315     if ((g1_promotion_region_alive_rate_ < PERCENT_100_D) && !this->IsFullGC() && !IsYoungFullGC()) {
316         size_t alive_bytes = region->CalcMarkBytes();
317         double alive_percentage = static_cast<double>(alive_bytes) / region->Size() * PERCENT_100_D;
318         if (alive_percentage >= g1_promotion_region_alive_rate_) {
319             return true;
320         }
321     }
322     return false;
323 }
324 
325 template <class LanguageConfig>
326 template <bool atomic, RegionFlag region_type>
RegionCompactingImpl(PandaVector<ObjectHeader * > * moved_objects,Region * region)327 void G1GC<LanguageConfig>::RegionCompactingImpl(PandaVector<ObjectHeader *> *moved_objects, Region *region)
328 {
329     auto object_allocator = this->GetG1ObjectAllocator();
330     size_t move_size = 0;
331     size_t move_count = 0;
332     size_t delete_size = 0;
333     size_t delete_count = 0;
334 
335     auto move_checker = [this, &moved_objects](ObjectHeader *src, ObjectHeader *dst) {
336         LOG_DEBUG_OBJECT_EVENTS << "MOVE object " << src << " -> " << dst;
337         ASSERT(ObjectToRegion(dst)->HasFlag(RegionFlag::IS_OLD));
338         this->SetForwardAddress(src, dst);
339         moved_objects->push_back(dst);
340     };
341     auto death_checker = [this, &move_count, &move_size, &delete_size, &delete_count](ObjectHeader *object_header) {
342         if (IsMarked(object_header)) {
343             ++move_count;
344             move_size += GetAlignedObjectSize(object_header->ObjectSize());
345 
346             return ObjectStatus::ALIVE_OBJECT;
347         }
348         ++delete_count;
349         delete_size += GetAlignedObjectSize(object_header->ObjectSize());
350 
351         // NOLINTNEXTLINE(readability-braces-around-statements)
352         if constexpr (region_type == RegionFlag::IS_EDEN) {
353             LOG_DEBUG_OBJECT_EVENTS << "DELETE YOUNG object " << object_header;
354             // NOLINTNEXTLINE(readability-misleading-indentation)
355         } else {
356             ASSERT(region_type == RegionFlag::IS_OLD);
357             LOG_DEBUG_OBJECT_EVENTS << "DELETE TENURED object " << object_header;
358         }
359         return ObjectStatus::DEAD_OBJECT;
360     };
361     // NOLINTNEXTLINE(readability-braces-around-statements)
362     if constexpr (region_type == RegionFlag::IS_EDEN) {
363         if (!this->NeedToPromote(region)) {
364             if (g1_track_freed_objects_) {
365                 // We want to track all freed objects, therefore, iterate over all objects in region.
366                 object_allocator->template CompactRegion<RegionFlag::IS_EDEN, false>(region, death_checker,
367                                                                                      move_checker);
368             } else {
369                 object_allocator->template CompactRegion<RegionFlag::IS_EDEN, true>(region, death_checker,
370                                                                                     move_checker);
371                 size_t allocated_size = region->GetAllocatedBytes();
372                 ASSERT(move_size <= allocated_size);
373                 // delete_count is equal to 0 because we don't track allocation in TLABs by a default.
374                 // We will do it only with PANDA_TRACK_TLAB_ALLOCATIONS key
375                 ASSERT(delete_count == 0);
376                 ASSERT(delete_size == 0);
377                 delete_size = allocated_size - move_size;
378             }
379             this->mem_stats_.template RecordSizeMovedYoung<atomic>(move_size);
380             this->mem_stats_.template RecordCountMovedYoung<atomic>(move_count);
381             this->mem_stats_.template RecordSizeFreedYoung<atomic>(delete_size);
382             this->mem_stats_.template RecordCountFreedYoung<atomic>(delete_count);
383         } else {
384             RegionPromotionImpl<atomic>(moved_objects, region);
385         }
386         // NOLINTNEXTLINE(readability-misleading-indentation)
387     } else {
388         ASSERT(region->HasFlag(RegionFlag::IS_OLD));
389         ASSERT(!region->HasFlag(RegionFlag::IS_NONMOVABLE) && !region->HasFlag(RegionFlag::IS_LARGE_OBJECT));
390         if (g1_track_freed_objects_) {
391             // We want to track all freed objects, therefore, iterate over all objects in region.
392             object_allocator->template CompactRegion<RegionFlag::IS_OLD, false>(region, death_checker, move_checker);
393         } else {
394             object_allocator->template CompactRegion<RegionFlag::IS_OLD, true>(region, death_checker, move_checker);
395             size_t allocated_objects = region->GetAllocatedObjects();
396             size_t allocated_size = region->GetAllocatedBytes();
397             ASSERT(move_count <= allocated_objects);
398             ASSERT(move_size <= allocated_size);
399             ASSERT(delete_count == 0);
400             ASSERT(delete_size == 0);
401             delete_count = allocated_objects - move_count;
402             delete_size = allocated_size - move_size;
403         }
404         this->mem_stats_.template RecordSizeMovedTenured<atomic>(move_size);
405         this->mem_stats_.template RecordCountMovedTenured<atomic>(move_count);
406         this->mem_stats_.template RecordSizeFreedTenured<atomic>(delete_size);
407         this->mem_stats_.template RecordCountFreedTenured<atomic>(delete_count);
408     }
409 }
410 
411 template <class LanguageConfig>
InitWorker(void ** worker_data)412 bool G1GC<LanguageConfig>::InitWorker(void **worker_data)
413 {
414     os::memory::LockHolder lock(unassigned_buffers_lock_);
415     ASSERT(!unassigned_buffers_.empty());
416     *worker_data = unassigned_buffers_.back();
417     unassigned_buffers_.pop_back();
418     return true;
419 }
420 
421 template <class LanguageConfig>
DestroyWorker(void * worker_data)422 void G1GC<LanguageConfig>::DestroyWorker(void *worker_data)
423 {
424     os::memory::LockHolder lock(unassigned_buffers_lock_);
425     unassigned_buffers_.push_back(reinterpret_cast<RefVector *>(worker_data));
426 }
427 
428 template <class LanguageConfig>
WorkerTaskProcessing(GCWorkersTask * task,void * worker_data)429 void G1GC<LanguageConfig>::WorkerTaskProcessing(GCWorkersTask *task, void *worker_data)
430 {
431     switch (task->GetType()) {
432         case GCWorkersTaskTypes::TASK_MARKING: {
433             auto objects_stack = task->GetMarkingStack();
434             MarkStackMixed(objects_stack);
435             ASSERT(objects_stack->Empty());
436             this->GetInternalAllocator()->Delete(objects_stack);
437             break;
438         }
439         case GCWorkersTaskTypes::TASK_REMARK: {
440             const ReferenceCheckPredicateT &ref_disable_pred = [this]([[maybe_unused]] const ObjectHeader *obj) {
441                 // dont process refs on conc-mark in G1, it can cause a high pause
442                 LOG(DEBUG, REF_PROC) << "Skip reference: " << obj
443                                      << " because it's G1 with phase: " << static_cast<int>(this->GetGCPhase());
444                 return false;
445             };
446             auto objects_stack = task->GetMarkingStack();
447             this->MarkStack(&marker_, objects_stack, ref_disable_pred, calc_live_bytes_);
448             ASSERT(objects_stack->Empty());
449             this->GetInternalAllocator()->Delete(objects_stack);
450             break;
451         }
452         case GCWorkersTaskTypes::TASK_REGION_COMPACTING: {
453             auto data = task->GetRegionData();
454             PandaVector<ObjectHeader *> *moved_objects = data->first;
455             Region *region = data->second;
456             if (region->HasFlag(RegionFlag::IS_EDEN)) {
457                 RegionCompactingImpl<true, RegionFlag::IS_EDEN>(moved_objects, region);
458             } else if (region->HasFlag(RegionFlag::IS_OLD)) {
459                 RegionCompactingImpl<true, RegionFlag::IS_OLD>(moved_objects, region);
460             } else {
461                 LOG(FATAL, GC) << "Unsupported region type";
462             }
463             this->GetInternalAllocator()->Delete(data);
464             break;
465         }
466         case GCWorkersTaskTypes::TASK_INIT_REFS_FROM_REMSETS: {
467             CardTable::CardPtr card = task->GetCard();
468             auto mem_range = this->GetCardTable()->GetMemoryRange(card);
469             Region *region = AddrToRegion(ToVoidPtr(mem_range.GetStartAddress()));
470             ASSERT(!region->HasFlag(IS_EDEN));
471             auto *refs_from_remsets = reinterpret_cast<RefVector *>(worker_data);
472             CollectRefsFromCard(card, region, refs_from_remsets);
473             break;
474         }
475         default:
476             LOG(FATAL, GC) << "Unimplemented for " << GCWorkersTaskTypesToString(task->GetType());
477             UNREACHABLE();
478     }
479 }
480 
481 template <class LanguageConfig>
UpdateCollectionSet(const CollectionSet & collectible_regions)482 void G1GC<LanguageConfig>::UpdateCollectionSet(const CollectionSet &collectible_regions)
483 {
484     collection_set_ = collectible_regions;
485     for (auto r : collection_set_) {
486         // we don't need to reset flag, because we don't reuse collection_set region
487         r->AddFlag(RegionFlag::IS_COLLECTION_SET);
488         LOG_DEBUG_GC << "dump region: " << *r;
489     }
490 }
491 
492 template <class LanguageConfig>
RunPhasesForRegions(panda::GCTask & task,const CollectionSet & collectible_regions)493 void G1GC<LanguageConfig>::RunPhasesForRegions(panda::GCTask &task, const CollectionSet &collectible_regions)
494 {
495     if (collectible_regions.empty()) {
496         LOG_DEBUG_GC << "No regions specified for collection " << task.reason_;
497     }
498     ASSERT(concurrent_marking_stack_.Empty());
499     this->GetObjectGenAllocator()->InvalidateSpaceData();
500     this->GetObjectGenAllocator()->UpdateSpaceData();
501     RunGC(task, collectible_regions);
502     collection_set_ = CollectionSet();  // We can use a pointer to vector here
503 }
504 
505 template <class LanguageConfig>
RunFullForTenured(panda::GCTask & task)506 void G1GC<LanguageConfig>::RunFullForTenured(panda::GCTask &task)
507 {
508     this->SetFullGC(true);
509     // At this point young is empty, we can mark+collect+move tenured as a whole
510     // A single free tenured region is enough to compact the whole tenured space
511     ASSERT(this->GetG1ObjectAllocator()->GetYoungRegions().empty());
512     ASSERT(HaveEnoughRegionsToMove(1));
513     LOG_DEBUG_GC << "Running Full gc for tenured";
514     auto tenured_set = GetCollectibleRegions(task, false);
515     RunFullMarkAndProcessRefs(task, tenured_set);
516 
517     // Collect regions 1 by 1
518     RunPhasesForRegions(task, tenured_set);
519 }
520 
521 template <class LanguageConfig>
RunFullMarkAndProcessRefs(panda::GCTask & task,const CollectionSet & collectible_regions)522 void G1GC<LanguageConfig>::RunFullMarkAndProcessRefs(panda::GCTask &task, const CollectionSet &collectible_regions)
523 {
524     this->SetFullGC(true);
525     LOG_DEBUG_GC << "Mark regions set size:" << collectible_regions.size();
526     UpdateCollectionSet(collectible_regions);
527     StartMarking<false>(task);
528     for (auto region : collectible_regions) {
529         region->RmvFlag(RegionFlag::IS_COLLECTION_SET);
530     }
531     collection_set_ = CollectionSet();  // We can use pointer to vector here
532 }
533 
534 template <class LanguageConfig>
RunFullProcessRefsNoCollect(panda::GCTask & task)535 void G1GC<LanguageConfig>::RunFullProcessRefsNoCollect(panda::GCTask &task)
536 {
537     this->SetFullGC(true);
538     LOG_DEBUG_GC << "Scannig full heap and process references";
539     auto scan_set = GetCollectibleRegions(task, false);
540     RunFullMarkAndProcessRefs(task, scan_set);
541 }
542 
543 template <class LanguageConfig>
RunPhasesImpl(panda::GCTask & task)544 void G1GC<LanguageConfig>::RunPhasesImpl(panda::GCTask &task)
545 {
546     interrupt_concurrent_flag_ = false;
547     LOG_DEBUG_GC << "G1GC start, reason: " << task.reason_;
548     LOG_DEBUG_GC << "Footprint before GC: " << this->GetPandaVm()->GetMemStats()->GetFootprintHeap();
549     task.UpdateGCCollectionType(GCCollectionType::YOUNG);
550 
551     uint64_t young_total_time {0};
552     this->GetTiming()->Reset();
553     size_t bytes_in_heap_before_move = this->GetPandaVm()->GetMemStats()->GetFootprintHeap();
554     {
555         ScopedTiming t("G1 GC", *this->GetTiming());
556         {
557             GCScopedPauseStats scoped_pause_stats(this->GetPandaVm()->GetGCStats());
558             this->mem_stats_.Reset();
559             if ((task.reason_ == GCTaskCause::YOUNG_GC_CAUSE) || (task.reason_ == GCTaskCause::OOM_CAUSE) ||
560                 (task.reason_ == GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE) ||
561                 (task.reason_ == GCTaskCause::STARTUP_COMPLETE_CAUSE) ||
562                 (task.reason_ == GCTaskCause::EXPLICIT_CAUSE) || (task.reason_ == GCTaskCause::NATIVE_ALLOC_CAUSE)) {
563                 this->GetPandaVm()->GetMemStats()->RecordGCPauseStart();
564                 // Check there is no concurrent mark running by another thread.
565                 // Atomic with relaxed order reason: concurrent access with another thread which can running GC now
566                 ASSERT(!concurrent_marking_flag_.load(std::memory_order_relaxed));
567 
568                 if ((task.reason_ == GCTaskCause::EXPLICIT_CAUSE) || (task.reason_ == GCTaskCause::OOM_CAUSE)) {
569                     this->SetFullGC(true);
570                 }
571                 WaitForUpdateRemsetThread();
572                 // Atomic with acquire order reason: to see changes made by GC thread (which do concurrent marking and
573                 // than set is_mixed_gc_required_) in mutator thread which waits for the end of concurrent marking.
574                 auto collectible_regions =
575                     GetCollectibleRegions(task, is_mixed_gc_required_.load(std::memory_order_acquire));
576                 if (this->IsFullGC()) {
577                     LOG_DEBUG_GC << "Explicit Full GC invocation due to a reason: " << task.reason_;
578                     // Clear young first. We tried to maintain enough regions for that
579                     // If the cause is OOM - most likely it is not true and GC will give up
580                     // GCing Young can be done using usual young collection routines
581                     // so no need to launch it with "full" flag.
582                     this->SetFullGC(false);
583                     // Issue 8183: Remove unnessesary SetYoungFullGC check after refactoring Full GC
584                     SetYoungFullGC(true);
585                     auto g1_allocator = this->GetG1ObjectAllocator();
586                     CollectionSet young_set(g1_allocator->GetYoungRegions());
587                     if (!young_set.empty() && HaveEnoughSpaceToMove(young_set)) {
588                         LOG_DEBUG_GC << "Collect young-space, size:" << young_set.size();
589                         UpdateCollectionSet(young_set);
590                         RunPhasesForRegions(task, young_set);
591                     }
592                     // Issue 8183: Remove unnessesary SetYoungFullGC check after refactoring Full GC
593                     SetYoungFullGC(false);
594 
595                     // To efficiently get rid of garbage, we only work with tenured space, doing full mark
596                     if (this->GetG1ObjectAllocator()->GetYoungRegions().empty() && HaveEnoughRegionsToMove(1)) {
597                         RunFullForTenured(task);
598                     } else {
599                         // We cannot move or delete any garbage at this point
600                         // However, some languages require some types of references being processed
601                         // at OOM. That is possible since it doesn't require any free space
602                         RunFullProcessRefsNoCollect(task);
603                         LOG_INFO_GC << "Failed to run gc, not enough free regions";
604                         LOG_INFO_GC << "Accounted total object used bytes = "
605                                     << PoolManager::GetMmapMemPool()->GetObjectUsedBytes();
606                     }
607                 } else if (!collectible_regions.empty() && HaveEnoughSpaceToMove(collectible_regions)) {
608                     // Ordinary collection flow
609                     time::Timer timer(&young_total_time, true);
610                     LOG_DEBUG_GC << "Collect regions size:" << collectible_regions.size();
611                     UpdateCollectionSet(collectible_regions);
612                     RunPhasesForRegions(task, collectible_regions);
613                     if (!HaveEnoughSpaceToMove(collectible_regions)) {
614                         LOG_DEBUG_GC << "Not leaving enough regions for next young/mixed collection. Proceed to "
615                                         "iterative implicit Full GC";
616                         // Slow path, full GC. We're short on free regions. In order to prevent OOM at the next GC,
617                         // try to free up enough regions so we can do mixed/young once again next time.
618                         // Here, we have a chance to compact heap so at the next GC mixed is going to have enough
619                         // regions to move to tenured. Without this step, we won't be able to do full at any time, since
620                         // we permanently won't have enough regions to move young to, thus not collecting anything
621                         RunFullForTenured(task);
622                     }
623                     if (!HaveEnoughSpaceToMove(collectible_regions)) {
624                         LOG_DEBUG_GC << "Implicit Full GC failed to free up enough space. Expect OOM GC soon";
625                         LOG_DEBUG_GC << "Accounted total object used bytes = "
626                                      << PoolManager::GetMmapMemPool()->GetObjectUsedBytes();
627                     }
628                     if (young_total_time > 0) {
629                         this->GetStats()->AddTimeValue(young_total_time, TimeTypeStats::YOUNG_TOTAL_TIME);
630                     }
631                 } else {
632                     LOG_DEBUG_GC << "Failed to run gc: "
633                                  << (collectible_regions.empty() ? "nothing to collect in movable space"
634                                                                  : "not enough free regions to move");
635                 }
636                 this->GetPandaVm()->GetMemStats()->RecordGCPauseEnd();
637             }
638         }
639         // Atomic with acquire order reason: to see changes made by GC thread (which do concurrent marking and than set
640         // is_mixed_gc_required_) in mutator thread which waits for the end of concurrent marking.
641         if (is_mixed_gc_required_.load(std::memory_order_acquire)) {
642             if (!HaveGarbageRegions()) {
643                 // Atomic with release order reason: to see changes made by GC thread (which do concurrent marking and
644                 // than set is_mixed_gc_required_) in mutator thread which waits for the end of concurrent marking.
645                 is_mixed_gc_required_.store(false, std::memory_order_release);
646             }
647         } else if (!interrupt_concurrent_flag_ && this->ShouldRunTenuredGC(task)) {
648             ASSERT(collection_set_.empty());
649             // Init concurrent marking
650             concurrent_marking_flag_ = true;
651         }
652         if (concurrent_marking_flag_ && !interrupt_concurrent_flag_) {
653             StartMarking<true>(task);
654             concurrent_marking_flag_ = false;
655             // interrupt_concurrent_flag_ may be set during concurrent marking.
656             if (!interrupt_concurrent_flag_) {
657                 Remark(task);
658                 // Enable mixed GC
659                 if (HaveGarbageRegions()) {
660                     // Atomic with release order reason: to see changes made by GC thread (which do concurrent marking
661                     // and than set is_mixed_gc_required_) in mutator thread which waits for the end of concurrent
662                     // marking.
663                     is_mixed_gc_required_.store(true, std::memory_order_release);
664                 }
665                 {
666                     ConcurrentScope concurrent_scope(this);
667                     CollectNonRegularObjects<true, true>(task);
668                 }
669             } else {
670                 ClearSatb();
671             }
672         }
673     }
674     // Update global and GC memstats based on generational memstats information
675     // We will update tenured stats and record allocations, so set 'true' values
676     this->UpdateMemStats(bytes_in_heap_before_move, true, true);
677 
678     LOG_DEBUG_GC << "Footprint after GC: " << this->GetPandaVm()->GetMemStats()->GetFootprintHeap();
679     this->SetFullGC(false);
680 }
681 
682 template <class LanguageConfig>
HaveGarbageRegions()683 bool G1GC<LanguageConfig>::HaveGarbageRegions()
684 {
685     // Use GetTopGarbageRegions because it doesn't return current regions
686     PandaVector<Region *> regions = GetG1ObjectAllocator()->template GetTopGarbageRegions<false>(1U);
687     if (regions.empty()) {
688         return false;
689     }
690     double garbage_rate = static_cast<double>(regions[0]->GetGarbageBytes()) / regions[0]->Size();
691     return garbage_rate >= region_garbage_rate_threshold_;
692 }
693 
694 template <class LanguageConfig>
InitializeImpl()695 void G1GC<LanguageConfig>::InitializeImpl()
696 {
697     // GC saved the PandaVM instance, so we get allocator from the PandaVM.
698     InternalAllocatorPtr allocator = this->GetInternalAllocator();
699     this->CreateCardTable(allocator, PoolManager::GetMmapMemPool()->GetMinObjectAddress(),
700                           PoolManager::GetMmapMemPool()->GetTotalObjectSize());
701 
702     // TODO(dtrubenkov): initialize barriers
703     auto barrier_set = allocator->New<GCG1BarrierSet>(
704         allocator, &concurrent_marking_flag_, &PreWrbFuncEntrypoint, &PostWrbUpdateCardFuncEntrypoint,
705         panda::helpers::math::GetIntLog2(this->GetG1ObjectAllocator()->GetRegionSize()), this->GetCardTable(),
706         updated_refs_queue_, &queue_lock_);
707     ASSERT(barrier_set != nullptr);
708     this->SetGCBarrierSet(barrier_set);
709 
710     if (this->IsWorkerThreadsExist()) {
711         auto thread_pool = allocator->New<GCWorkersThreadPool>(allocator, this, this->GetSettings()->GCWorkersCount());
712         ASSERT(thread_pool != nullptr);
713         this->SetWorkersPool(thread_pool);
714     }
715     {
716         // to make TSAN happy because we access updated_refs_queue_ inside constructor of UpdateRemsetThread
717         os::memory::LockHolder lock(queue_lock_);
718         update_remset_thread_ = allocator->template New<UpdateRemsetThread<LanguageConfig>>(
719             this, this->GetPandaVm(), updated_refs_queue_, &queue_lock_, this->GetG1ObjectAllocator()->GetRegionSize(),
720             this->GetSettings()->G1EnableConcurrentUpdateRemset(), this->GetSettings()->G1MinConcurrentCardsToProcess(),
721             this->GetCardTable());
722     }
723     ASSERT(update_remset_thread_ != nullptr);
724     LOG_DEBUG_GC << "G1GC initialized";
725 }
726 
727 template <class LanguageConfig>
MarkObject(ObjectHeader * object)728 void G1GC<LanguageConfig>::MarkObject(ObjectHeader *object)
729 {
730     marker_.Mark(object);
731 }
732 
733 template <class LanguageConfig>
MarkObjectIfNotMarked(ObjectHeader * object)734 bool G1GC<LanguageConfig>::MarkObjectIfNotMarked(ObjectHeader *object)
735 {
736     ASSERT(object != nullptr);
737     return marker_.MarkIfNotMarked(object);
738 }
739 
740 template <class LanguageConfig>
InitGCBitsForAllocationInTLAB(panda::ObjectHeader * object)741 void G1GC<LanguageConfig>::InitGCBitsForAllocationInTLAB([[maybe_unused]] panda::ObjectHeader *object)
742 {
743     LOG(FATAL, GC) << "Not implemented";
744 }
745 
746 template <class LanguageConfig>
IsMarked(panda::ObjectHeader const * object) const747 bool G1GC<LanguageConfig>::IsMarked(panda::ObjectHeader const *object) const
748 {
749     return marker_.IsMarked(object);
750 }
751 
752 template <class LanguageConfig>
MarkStackMixed(GCMarkingStackType * stack)753 void G1GC<LanguageConfig>::MarkStackMixed(GCMarkingStackType *stack)
754 {
755     auto ref_pred = [this](const ObjectHeader *obj) { return InGCSweepRange(obj); };
756     auto mark_pred = [this](const ObjectHeader *obj) { return InGCSweepRange(obj); };
757     this->MarkStack(&marker_, stack, ref_pred, mark_pred);
758 }
759 
760 template <class LanguageConfig>
MarkStackFull(GCMarkingStackType * stack)761 void G1GC<LanguageConfig>::MarkStackFull(GCMarkingStackType *stack)
762 {
763     auto ref_pred = []([[maybe_unused]] const ObjectHeader *obj) { return true; };
764     auto mark_pred = []([[maybe_unused]] const ObjectHeader *obj) { return true; };
765     this->MarkStack(&marker_, stack, ref_pred, mark_pred);
766 }
767 
768 template <class LanguageConfig>
MarkReferences(GCMarkingStackType * references,GCPhase gc_phase)769 void G1GC<LanguageConfig>::MarkReferences(GCMarkingStackType *references, GCPhase gc_phase)
770 {
771     trace::ScopedTrace scoped_trace(__FUNCTION__);
772     LOG_DEBUG_GC << "Start marking " << references->Size() << " references";
773     // mark refs only on mixed-gc and on full_gc. On concurrent mark we don't handle any references
774     if (gc_phase == GCPhase::GC_PHASE_MARK_YOUNG || this->IsFullGC()) {
775         MarkStackMixed(references);
776     } else if (gc_phase == GCPhase::GC_PHASE_INITIAL_MARK || gc_phase == GCPhase::GC_PHASE_MARK ||
777                gc_phase == GCPhase::GC_PHASE_REMARK) {
778         // nothing
779     } else {
780         LOG_DEBUG_GC << "phase: " << GCScopedPhase::GetPhaseName(gc_phase);
781         UNREACHABLE();
782     }
783 }
784 
785 template <class LanguageConfig>
InGCSweepRange(const ObjectHeader * object) const786 bool G1GC<LanguageConfig>::InGCSweepRange(const ObjectHeader *object) const
787 {
788     [[maybe_unused]] auto phase = this->GetGCPhase();
789     ASSERT_DO(!this->collection_set_.empty() || this->IsFullGC() || phase == GCPhase::GC_PHASE_REMARK,
790               std::cerr << "Incorrect phase in InGCSweepRange: " << static_cast<size_t>(phase) << "\n");
791 
792     ASSERT(PoolManager::GetMmapMemPool()->GetSpaceTypeForAddr(object) == SpaceType::SPACE_TYPE_OBJECT ||
793            PoolManager::GetMmapMemPool()->GetSpaceTypeForAddr(object) == SpaceType::SPACE_TYPE_NON_MOVABLE_OBJECT ||
794            PoolManager::GetMmapMemPool()->GetSpaceTypeForAddr(object) == SpaceType::SPACE_TYPE_HUMONGOUS_OBJECT);
795     Region *obj_region = ObjectToRegion(object);
796     return obj_region->IsInCollectionSet();
797 }
798 
799 template <class LanguageConfig>
RunGC(GCTask & task,const CollectionSet & collectible_regions)800 void G1GC<LanguageConfig>::RunGC(GCTask &task, const CollectionSet &collectible_regions)
801 {
802     GCScope<TRACE_TIMING> scoped_trace(__FUNCTION__, this);
803     LOG_DEBUG_GC << "GC start";
804     uint64_t young_pause_time;
805     {
806         // TODO: Measure only those that are on pause
807         time::Timer timer(&young_pause_time, true);
808         if (is_mixed_gc_required_) {
809             LOG_DEBUG_GC << "Mixed GC";
810             task.collection_type_ = GCCollectionType::MIXED;
811         } else {
812             task.collection_type_ = GCCollectionType::YOUNG;
813         }
814 
815         if (!this->IsFullGC()) {
816             CacheRefsFromRemsets();
817             MixedMark(task, collectible_regions);
818             CollectAndMove(collectible_regions);
819             this->GetCardTable()->ClearAll();
820             ClearRefsFromRemsetsCache();
821         } else {
822             task.collection_type_ = GCCollectionType::FULL;
823             for (auto r : collectible_regions) {
824                 LOG_DEBUG_GC << "Iterative full GC collecting region " << *r;
825                 CollectionSet cs {};
826                 cs.AddRegion(r);
827                 UpdateCollectionSet(cs);
828                 CacheRefsFromRemsets();
829                 CollectAndMove(cs);
830                 ClearRefsFromRemsetsCache();
831                 this->GetCardTable()->ClearAll();
832             }
833         }
834         this->GetObjectGenAllocator()->InvalidateSpaceData();
835     }
836     if (young_pause_time > 0) {
837         this->GetStats()->AddTimeValue(young_pause_time, TimeTypeStats::YOUNG_PAUSED_TIME);
838     }
839     LOG_DEBUG_GC << "G1GC RunGC end";
840 }
841 
842 template <class LanguageConfig>
MixedMark(const GCTask & task,const CollectionSet & collectible_regions)843 void G1GC<LanguageConfig>::MixedMark(const GCTask &task, const CollectionSet &collectible_regions)
844 {
845     GCScope<TRACE_TIMING_PHASE> scoped_trace(__FUNCTION__, this, GCPhase::GC_PHASE_MARK_YOUNG);
846 
847     bool use_gc_workers = this->GetSettings()->ParallelMarkingEnabled();
848 
849     GCMarkingStackType objects_stack(this, use_gc_workers ? this->GetSettings()->GCRootMarkingStackMaxSize() : 0,
850                                      use_gc_workers ? this->GetSettings()->GCWorkersMarkingStackMaxSize() : 0,
851                                      GCWorkersTaskTypes::TASK_MARKING);
852     // Iterate over roots and add other roots
853     // 0. Pre-process refs queue and fill RemSets (should be done later in background)
854     // Note: We need to process only tenured -> young refs,
855     // since we reach this by graph from tenured roots,
856     // because we will process all young regions at young GC we will find all required references
857 
858     ASSERT(this->GetReferenceProcessor()->GetReferenceQueueSize() ==
859            0);  // all references should be processed on previous-gc
860 
861     auto ref_pred = [this](const ObjectHeader *obj) { return this->InGCSweepRange(obj); };
862     GCRootVisitor gc_mark_collection_set = [&objects_stack, this, &ref_pred](const GCRoot &gc_root) {
863         ObjectHeader *root_object = gc_root.GetObjectHeader();
864         ObjectHeader *from_object = gc_root.GetFromObjectHeader();
865         LOG_DEBUG_GC << "Handle root " << GetDebugInfoAboutObject(root_object) << " from: " << gc_root.GetType();
866         if (UNLIKELY(from_object != nullptr) &&
867             this->IsReference(from_object->ClassAddr<BaseClass>(), from_object, ref_pred)) {
868             LOG_DEBUG_GC << "Add reference: " << GetDebugInfoAboutObject(from_object) << " to stack";
869             marker_.Mark(from_object);
870             this->ProcessReference(&objects_stack, from_object->ClassAddr<BaseClass>(), from_object,
871                                    GC::EmptyReferenceProcessPredicate);
872         } else {
873             // Skip non-collection-set roots
874             auto root_object_ptr = gc_root.GetObjectHeader();
875             ASSERT(root_object_ptr != nullptr);
876             if (this->InGCSweepRange(root_object_ptr)) {
877                 LOG_DEBUG_GC << "root " << GetDebugInfoAboutObject(root_object_ptr);
878                 if (marker_.MarkIfNotMarked(root_object_ptr)) {
879                     objects_stack.PushToStack(gc_root.GetType(), root_object_ptr);
880                 }
881             } else {
882                 LOG_DEBUG_GC << "Skip root for young mark: " << std::hex << root_object_ptr;
883             }
884         }
885     };
886     {
887         GCScope<TRACE_TIMING> marking_collection_set_roots_trace("Marking roots collection-set", this);
888 
889         for (Region *region : collectible_regions) {
890             region->GetMarkBitmap()->ClearAllBits();
891         }
892 
893         this->VisitRoots(gc_mark_collection_set, VisitGCRootFlags::ACCESS_ROOT_NONE);
894         // Visit roots from RemSets
895         auto field_visitor = [this, &objects_stack]([[maybe_unused]] ObjectHeader *from_obj, ObjectHeader *to_obj,
896                                                     [[maybe_unused]] uint32_t offset,
897                                                     [[maybe_unused]] bool is_volatile) {
898             if (!InGCSweepRange(to_obj)) {
899                 LOG_DEBUG_GC << "Skip root for collection_set mark: " << std::hex << to_obj;
900                 return true;
901             }
902             LOG_DEBUG_GC << "root " << GetDebugInfoAboutObject(to_obj);
903             if (marker_.MarkIfNotMarked(to_obj)) {
904                 objects_stack.PushToStack(from_obj, to_obj);
905             }
906             return true;
907         };
908         VisitRemSets(field_visitor);
909     }
910     {
911         ScopedTiming mark_stack_timing("MarkStack", *this->GetTiming());
912         this->MarkStackMixed(&objects_stack);
913         ASSERT(objects_stack.Empty());
914         if (use_gc_workers) {
915             this->GetWorkersPool()->WaitUntilTasksEnd();
916         }
917     }
918 
919     auto ref_clear_pred = [this]([[maybe_unused]] const ObjectHeader *obj) { return this->InGCSweepRange(obj); };
920     this->GetPandaVm()->HandleReferences(task, ref_clear_pred);
921     // HandleReferences could write a new barriers - so we need to handle them before moving
922     WaitForUpdateRemsetThread();
923 }
924 
925 template <class LanguageConfig>
CollectVerificationInfo(const CollectionSet & collection_set)926 HeapVerifierIntoGC<LanguageConfig> G1GC<LanguageConfig>::CollectVerificationInfo(const CollectionSet &collection_set)
927 {
928     HeapVerifierIntoGC<LanguageConfig> collect_verifier(this->GetPandaVm()->GetHeapManager());
929     if (this->GetSettings()->IntoGCHeapVerification()) {
930         ScopedTiming collect_verification_timing(__FUNCTION__, *this->GetTiming());
931         PandaVector<MemRange> mem_ranges;
932         mem_ranges.reserve(collection_set.size());
933         std::for_each(collection_set.begin(), collection_set.end(),
934                       [&mem_ranges](const Region *region) { mem_ranges.emplace_back(region->Begin(), region->End()); });
935         collect_verifier.CollectVerificationInfo(std::move(mem_ranges));
936     }
937     return collect_verifier;
938 }
939 
940 template <class LanguageConfig>
VerifyCollectAndMove(HeapVerifierIntoGC<LanguageConfig> && collect_verifier,const CollectionSet & collection_set)941 void G1GC<LanguageConfig>::VerifyCollectAndMove(HeapVerifierIntoGC<LanguageConfig> &&collect_verifier,
942                                                 const CollectionSet &collection_set)
943 {
944     if (this->GetSettings()->IntoGCHeapVerification()) {
945         ScopedTiming verification_timing(__FUNCTION__, *this->GetTiming());
946         PandaVector<MemRange> alive_mem_range;
947         std::for_each(collection_set.begin(), collection_set.end(), [&alive_mem_range](const Region *region) {
948             if (region->HasFlag(RegionFlag::IS_PROMOTED)) {
949                 alive_mem_range.emplace_back(region->Begin(), region->End());
950             }
951         });
952         size_t fails_count = collect_verifier.VerifyAll(std::move(alive_mem_range));
953         if (this->GetSettings()->FailOnHeapVerification() && fails_count > 0U) {
954             LOG(FATAL, GC) << "Heap was corrupted during CollectAndMove GC phase, HeapVerifier found " << fails_count
955                            << " corruptions";
956         }
957     }
958 }
959 
960 template <class LanguageConfig>
961 // NOLINTNEXTLINE(readability-function-size)
CollectAndMove(const CollectionSet & collection_set)962 bool G1GC<LanguageConfig>::CollectAndMove(const CollectionSet &collection_set)
963 {
964     GCScope<TRACE_PHASE> scope(__FUNCTION__, this, GCPhase::GC_PHASE_COLLECT_YOUNG_AND_MOVE);
965     LOG_DEBUG_GC << "== G1GC CollectAndMove start ==";
966     auto internal_allocator = this->GetInternalAllocator();
967     bool use_gc_workers = this->GetSettings()->ParallelCompactingEnabled() && !this->IsFullGC();
968 
969     PandaVector<PandaVector<ObjectHeader *> *> moved_objects_vector;
970     HeapVerifierIntoGC<LanguageConfig> collect_verifier = this->CollectVerificationInfo(collection_set);
971     {
972         ScopedTiming compact_regions("CompactRegions", *this->GetTiming());
973         if (!use_gc_workers) {
974             auto vector = internal_allocator->template New<PandaVector<ObjectHeader *>>();
975             moved_objects_vector.push_back(vector);
976         }
977         for (auto r : collection_set.Young()) {
978             this->DoRegionCompacting<RegionFlag::IS_EDEN>(r, use_gc_workers, &moved_objects_vector);
979         }
980         for (auto r : collection_set.Tenured()) {
981             this->DoRegionCompacting<RegionFlag::IS_OLD>(r, use_gc_workers, &moved_objects_vector);
982         }
983 
984         if (use_gc_workers) {
985             this->GetWorkersPool()->WaitUntilTasksEnd();
986         }
987     }
988 
989     PandaVector<Region *> tenured_regions(collection_set.Tenured().begin(), collection_set.Tenured().end());
990     UpdateRefsToMovedObjects(&moved_objects_vector);
991     this->VerifyCollectAndMove(std::move(collect_verifier), collection_set);
992     this->SweepStringTableYoung([this](ObjectHeader *obj) { return this->InGCSweepRange(obj); });
993 
994     ActualizeRemSets();
995 
996     auto object_allocator = this->GetG1ObjectAllocator();
997     object_allocator->ResetYoungAllocator();
998     object_allocator->template ResetRegions<RegionFlag::IS_OLD>(tenured_regions);
999 
1000     // Don't forget to delete all temporary elements
1001     if (use_gc_workers) {
1002         for (auto r : moved_objects_vector) {
1003             internal_allocator->Delete(r);
1004         }
1005     } else {
1006         ASSERT(moved_objects_vector.size() == 1);
1007         internal_allocator->Delete(moved_objects_vector.back());
1008     }
1009 
1010     LOG_DEBUG_GC << "== G1GC CollectAndMove end ==";
1011     return true;
1012 }
1013 
1014 template <class LanguageConfig>
UpdateRefsToMovedObjects(PandaVector<PandaVector<ObjectHeader * > * > * moved_objects_vector)1015 void G1GC<LanguageConfig>::UpdateRefsToMovedObjects(PandaVector<PandaVector<ObjectHeader *> *> *moved_objects_vector)
1016 {
1017     GCScope<TRACE_TIMING> scope("UpdateRefsToMovedObjects", this);
1018     auto object_allocator = this->GetG1ObjectAllocator();
1019     size_t region_size_bits = panda::helpers::math::GetIntLog2(object_allocator->GetRegionSize());
1020     auto update_refs = [region_size_bits](ObjectHeader *object, ObjectHeader *ref, uint32_t offset, bool is_volatile) {
1021         ObjectHeader *forwarded =
1022             ObjectHelpers<LanguageConfig::LANG_TYPE>::UpdateRefToMovedObject(object, ref, offset, is_volatile);
1023         if (((ToUintPtr(object) ^ ToUintPtr(forwarded)) >> region_size_bits) != 0) {
1024             RemSet<>::AddRefWithAddr<false>(object, forwarded);
1025         }
1026         return true;
1027     };
1028 
1029     // Update references exyoung -> young
1030     LOG_DEBUG_GC << "=== Update exyoung -> young references. START. ===";
1031     for (auto moved_objects : *moved_objects_vector) {
1032         for (auto obj : *moved_objects) {
1033             ObjectHelpers<LanguageConfig::LANG_TYPE>::TraverseAllObjectsWithInfo(obj, update_refs);
1034         }
1035     }
1036 
1037     LOG_DEBUG_GC << "=== Update exyoung -> young references. END. ===";
1038     // update references tenured -> young
1039     LOG_DEBUG_GC << "=== Update tenured -> young references. START. ===";
1040 
1041     VisitRemSets(update_refs);
1042     LOG_DEBUG_GC << "=== Update tenured -> young references. END. ===";
1043     this->CommonUpdateRefsToMovedObjects();
1044 }
1045 
1046 template <class LanguageConfig>
1047 template <bool is_concurrent>
StartMarking(panda::GCTask & task)1048 void G1GC<LanguageConfig>::StartMarking(panda::GCTask &task)
1049 {
1050     auto object_allocator = GetG1ObjectAllocator();
1051     PandaString mark_type;
1052     // NOLINTNEXTLINE(readability-braces-around-statements)
1053     if constexpr (is_concurrent) {
1054         mark_type = "Concurrent";
1055     } else {  // NOLINT(readability-misleading-indentation)
1056         mark_type = "OnPause";
1057     }
1058 
1059     {
1060         // First we need to unmark all heap
1061         ScopedTiming un_mark_timing("UnMark", *this->GetTiming());
1062         LOG_DEBUG_GC << "Start unmark all heap before " << mark_type << " mark";
1063         auto all_region = object_allocator->GetAllRegions();
1064         for (Region *r : all_region) {
1065             auto *bitmap = r->GetMarkBitmap();
1066             // unmark full-heap except Humongous-space
1067             bitmap->ClearAllBits();
1068         }
1069 #ifndef NDEBUG
1070         this->GetObjectAllocator()->IterateOverObjects(
1071             [this](ObjectHeader *obj) { ASSERT(!this->marker_.IsMarked(obj)); });
1072 #endif
1073     }
1074     ASSERT(this->GetReferenceProcessor()->GetReferenceQueueSize() ==
1075            0);  // all references should be processed on mixed-gc
1076     LOG_DEBUG_GC << mark_type << " marking started";
1077     {
1078         GCScopedPhase scoped_phase(this->GetPandaVm()->GetMemStats(), this, GCPhase::GC_PHASE_INITIAL_MARK);
1079         // Collect non-heap roots.
1080         // Mark the whole heap by using only these roots.
1081         // The interregion roots will be processed at pause
1082 
1083         // InitialMark. STW
1084         ASSERT(concurrent_marking_stack_.Empty());
1085         auto &conc_stack = concurrent_marking_stack_;
1086         GCRootVisitor gc_mark_roots = [this, &conc_stack](const GCRoot &gc_root) {
1087             ValidateObject(gc_root.GetType(), gc_root.GetObjectHeader());
1088             if (marker_.MarkIfNotMarked(gc_root.GetObjectHeader())) {
1089                 conc_stack.PushToStack(gc_root.GetType(), gc_root.GetObjectHeader());
1090             }
1091         };
1092         this->VisitRoots(gc_mark_roots, VisitGCRootFlags::ACCESS_ROOT_ALL);
1093     }
1094     // Concurrent/on-pause marking
1095     {
1096         this->GetPandaVm()->GetMemStats()->RecordGCPauseEnd();
1097         // NOLINTNEXTLINE(readability-braces-around-statements)
1098         if constexpr (is_concurrent) {
1099             const ReferenceCheckPredicateT &disable_ref_pred = []([[maybe_unused]] const ObjectHeader *obj) {
1100                 return false;
1101             };
1102             auto interrupt_checker = [this]() {
1103                 return !concurrent_marking_stack_.Empty() && !interrupt_concurrent_flag_;
1104             };
1105             ConcurrentMark(
1106                 &marker_, &concurrent_marking_stack_, CardTableVisitFlag::VISIT_DISABLED, interrupt_checker,
1107                 disable_ref_pred, [&object_allocator](MemRange &mem_range) {
1108                     return !object_allocator->IsIntersectedWithYoung(  // CODECHECK-NOLINT(C_RULE_ID_INDENT_CHECK)
1109                         mem_range);                                    // CODECHECK-NOLINT(C_RULE_ID_INDENT_CHECK)
1110                 });                                                    // CODECHECK-NOLINT(C_RULE_ID_INDENT_CHECK)
1111             // weak refs shouldn't be added to the queue on concurrent-mark
1112             ASSERT(this->GetReferenceProcessor()->GetReferenceQueueSize() == 0);
1113         } else {  // NOLINT(readability-misleading-indentation)
1114             const ReferenceCheckPredicateT &ref_pred = []([[maybe_unused]] const ObjectHeader *obj) { return true; };
1115             auto no_concurrent_interrupt_checker = [this]() { return !concurrent_marking_stack_.Empty(); };
1116             this->OnPauseMark(
1117                 &marker_, &concurrent_marking_stack_, CardTableVisitFlag::VISIT_DISABLED,
1118                 no_concurrent_interrupt_checker, ref_pred, [&object_allocator](MemRange &mem_range) {
1119                     return !object_allocator->IsIntersectedWithYoung(  // CODECHECK-NOLINT(C_RULE_ID_INDENT_CHECK)
1120                         mem_range);                                    // CODECHECK-NOLINT(C_RULE_ID_INDENT_CHECK)
1121                 });                                                    // CODECHECK-NOLINT(C_RULE_ID_INDENT_CHECK)
1122 
1123             {
1124                 // we process all refs on FULL_GC
1125                 GCScopedPhase scoped_phase(this->GetPandaVm()->GetMemStats(), this, GCPhase::GC_PHASE_MARK);
1126                 ASSERT(this->IsFullGC());
1127                 auto g1_allocator = this->GetG1ObjectAllocator();
1128                 auto all_regions = g1_allocator->GetAllRegions();
1129                 /**
1130                  * We don't collect non-movable regions right now, if there was a reference from non-movable to
1131                  * young/tenured region then we reset markbitmap for non-nonmovable, but don't update livebitmap and we
1132                  * can traverse over non-reachable object (in CacheRefsFromRemsets) and visit DEAD object in
1133                  * tenured space (was delete on young-collection or in Iterative-full-gc phase.
1134                  */
1135                 // TODO(alovkov): add ASSERT(g1_allocator->GetRegions().size == collection_set_.size())
1136                 auto ref_clear_pred = []([[maybe_unused]] const ObjectHeader *obj) { return true; };
1137                 this->GetPandaVm()->HandleReferences(task, ref_clear_pred);
1138                 // HandleReferences could write a new barriers - so we need to handle them before cloning maps
1139                 WaitForUpdateRemsetThread();
1140                 // TODO(alovkov): make iterations over collection_set when non-movable regions will be supported
1141                 for (const auto &r : all_regions) {
1142                     if (r->GetLiveBitmap() != nullptr) {
1143                         r->CloneMarkBitmapToLiveBitmap();
1144                     }
1145                 }
1146                 CollectNonRegularObjects<false, false>(task);
1147             }
1148         }
1149         if (interrupt_concurrent_flag_) {
1150             concurrent_marking_stack_.Clear();
1151         }
1152         ASSERT(concurrent_marking_stack_.Empty());
1153         this->GetPandaVm()->GetMemStats()->RecordGCPauseStart();
1154     }
1155     ASSERT(concurrent_marking_stack_.Empty());
1156 }
1157 
1158 template <class LanguageConfig>
ConcurrentMark(Marker * marker,GCMarkingStackType * objects_stack,CardTableVisitFlag visit_card_table_roots,const ConcurrentMarkPredicateT & pred,const ReferenceCheckPredicateT & ref_pred,const MemRangeChecker & mem_range_checker)1159 void G1GC<LanguageConfig>::ConcurrentMark(Marker *marker, GCMarkingStackType *objects_stack,
1160                                           CardTableVisitFlag visit_card_table_roots,
1161                                           const ConcurrentMarkPredicateT &pred,
1162                                           const ReferenceCheckPredicateT &ref_pred,
1163                                           const MemRangeChecker &mem_range_checker)
1164 {
1165     GCScope<TRACE_TIMING_PHASE> scope(__FUNCTION__, this, GCPhase::GC_PHASE_MARK);
1166     ConcurrentScope concurrent_scope(this);
1167     this->MarkImpl(marker, objects_stack, visit_card_table_roots, pred, ref_pred, mem_range_checker);
1168     if (interrupt_concurrent_flag_) {
1169         return;
1170     }
1171     CalcLiveBytesForMovableTenuredRegions();
1172 }
1173 
1174 template <class LanguageConfig>
Remark(panda::GCTask const & task)1175 void G1GC<LanguageConfig>::Remark(panda::GCTask const &task)
1176 {
1177     /**
1178      * Make remark on pause to have all marked objects in tenured space, it gives possibility to check objects in
1179      * remsets. If they are not marked - we don't process this object, because it's dead already
1180      */
1181     GCScope<TIMING_PHASE> gc_scope(__FUNCTION__, this, GCPhase::GC_PHASE_REMARK);
1182     // TODO(alovkov): extract method for creation stack
1183     bool use_gc_workers = this->GetSettings()->ParallelMarkingEnabled();
1184     GCMarkingStackType stack(this, use_gc_workers ? this->GetSettings()->GCRootMarkingStackMaxSize() : 0,
1185                              use_gc_workers ? this->GetSettings()->GCWorkersMarkingStackMaxSize() : 0,
1186                              GCWorkersTaskTypes::TASK_REMARK);
1187 
1188     // The mutator may create new regions.
1189     // If so we should bind bitmaps of new regions.
1190     DrainSatb(&stack);
1191     const ReferenceCheckPredicateT &ref_disable_pred = [this]([[maybe_unused]] const ObjectHeader *obj) {
1192         LOG(DEBUG, REF_PROC) << "Skip reference: " << obj
1193                              << " because it's G1 with phase: " << static_cast<int>(this->GetGCPhase());
1194         return false;
1195     };
1196     this->MarkStack(&marker_, &stack, ref_disable_pred, calc_live_bytes_);
1197 
1198     if (use_gc_workers) {
1199         this->GetWorkersPool()->WaitUntilTasksEnd();
1200     }
1201     {
1202         ScopedTiming remsetThreadTiming("RemsetThread WaitUntilTasksEnd", *this->GetTiming());
1203         WaitForUpdateRemsetThread();
1204     }
1205 
1206     // ConcurrentMark doesn't visit young objects - so we can't clear references which are in young-space because we
1207     // don't know which objects are marked. We will process them on young/mixed GC separately later, here we process
1208     // only refs in tenured-space
1209     auto ref_clear_pred = []([[maybe_unused]] const ObjectHeader *obj) {
1210         return !ObjectToRegion(obj)->HasFlag(RegionFlag::IS_EDEN);
1211     };
1212     this->GetPandaVm()->HandleReferences(task, ref_clear_pred);
1213 
1214     auto g1_allocator = this->GetG1ObjectAllocator();
1215     auto all_regions = g1_allocator->GetAllRegions();
1216     for (const auto &region : all_regions) {
1217         // TODO(alovkov): set IS_OLD for NON_MOVABLE region when we create it
1218         if (region->HasFlag(IS_OLD) || region->HasFlag(IS_NONMOVABLE)) {
1219             region->SwapMarkBitmap();
1220         }
1221     }
1222 }
1223 
1224 template <class LanguageConfig>
GetCollectibleRegions(panda::GCTask const & task,bool is_mixed)1225 CollectionSet G1GC<LanguageConfig>::GetCollectibleRegions(panda::GCTask const &task, bool is_mixed)
1226 {
1227     ScopedTiming scoped_timing(__FUNCTION__, *this->GetTiming());
1228     // FillRemSet should be always finished before GetCollectibleRegions
1229     ASSERT(update_remset_thread_->GetQueueSize() == 0);
1230     auto g1_allocator = this->GetG1ObjectAllocator();
1231     LOG_DEBUG_GC << "Start GetCollectibleRegions is_mixed: " << is_mixed << " reason: " << task.reason_;
1232     CollectionSet collection_set(g1_allocator->GetYoungRegions());
1233     bool is_full_gc = this->IsFullGC();
1234     if (is_mixed || is_full_gc) {
1235         if (is_full_gc) {
1236             auto all_movable_regions = g1_allocator->GetMovableRegions();
1237             LOG_DEBUG_GC << "all movable region size: " << all_movable_regions.size();
1238             for (const auto &region : all_movable_regions) {
1239                 LOG_DEBUG_GC << "region: " << *region;
1240                 if (region->HasFlag(IS_EDEN)) {
1241                     continue;
1242                 }
1243                 ASSERT(!region->HasFlag(IS_NONMOVABLE) && !region->HasFlag(IS_LARGE_OBJECT));
1244                 ASSERT(region->HasFlag(IS_OLD));
1245                 collection_set.AddRegion(region);
1246             }
1247             // make new region to move objects there
1248             g1_allocator->ClearCurrentRegion();
1249         } else {
1250             auto garbage_regions = g1_allocator->template GetTopGarbageRegions<false>(number_of_mixed_tenured_regions_);
1251             for (auto garbage_region : garbage_regions) {
1252                 ASSERT(!garbage_region->HasFlag(IS_EDEN));
1253                 ASSERT(is_mixed_gc_required_);  // to be sure that GetLiveBytes is calculated in concurrent
1254                 double garbage_rate = static_cast<double>(garbage_region->GetGarbageBytes()) / garbage_region->Size();
1255                 if (garbage_rate >= region_garbage_rate_threshold_) {
1256                     LOG_DEBUG_GC << "Garbage percentage in " << std::hex << garbage_region << " region = " << std::dec
1257                                  << garbage_rate << " %, add to collection set";
1258                     collection_set.AddRegion(garbage_region);
1259                 } else {
1260                     LOG_DEBUG_GC << "Garbage percentage in " << std::hex << garbage_region << " region = " << std::dec
1261                                  << garbage_rate << " %, don't add to collection set";
1262                     break;
1263                 }
1264             }
1265         }
1266     }
1267     LOG_DEBUG_GC << "collectible_regions size: " << collection_set.size() << " reason: " << task.reason_
1268                  << " is_mixed: " << is_mixed;
1269     return collection_set;
1270 }
1271 
1272 template <class LanguageConfig>
CalcLiveBytesForMovableTenuredRegions()1273 void G1GC<LanguageConfig>::CalcLiveBytesForMovableTenuredRegions()
1274 {
1275     ScopedTiming scoped_timing(__FUNCTION__, *this->GetTiming());
1276     auto object_allocator = this->GetG1ObjectAllocator();
1277     auto movable_region = object_allocator->GetMovableRegions();
1278     for (const auto &region : movable_region) {
1279         if (region->HasFlag(IS_OLD)) {
1280             region->SetLiveBytes(region->CalcMarkBytes());
1281         }
1282     }
1283 }
1284 
1285 template <class LanguageConfig>
HaveEnoughSpaceToMove(const CollectionSet & collectible_regions)1286 bool G1GC<LanguageConfig>::HaveEnoughSpaceToMove(const CollectionSet &collectible_regions)
1287 {
1288     // Take parallel compacting into account
1289     size_t parallel_compacting_fragmentation =
1290         this->GetSettings()->ParallelCompactingEnabled() ? this->GetSettings()->GCWorkersCount() : 0;
1291     size_t required_regions = collectible_regions.Movable().size() + parallel_compacting_fragmentation;
1292     return HaveEnoughRegionsToMove(required_regions);
1293 }
1294 
1295 template <class LanguageConfig>
HaveEnoughRegionsToMove(size_t num)1296 bool G1GC<LanguageConfig>::HaveEnoughRegionsToMove(size_t num)
1297 {
1298     return GetG1ObjectAllocator()->HaveTenuredSize(num) && GetG1ObjectAllocator()->HaveFreeRegions(num);
1299 }
1300 
1301 template <class LanguageConfig>
OnThreadTerminate(ManagedThread * thread)1302 void G1GC<LanguageConfig>::OnThreadTerminate(ManagedThread *thread)
1303 {
1304     auto tid = thread->GetId();
1305     LOG_DEBUG_GC << "Call OnThreadTerminate for thread: " << tid;
1306     {
1307         os::memory::LockHolder lock(satb_and_newobj_buf_lock_);
1308         auto pre_buff = thread->MovePreBuff();
1309         ASSERT(pre_buff != nullptr);
1310         satb_buff_list_.push_back(pre_buff);
1311     }
1312     {
1313         os::memory::LockHolder lock(queue_lock_);
1314         auto *local_buffer = thread->GetG1PostBarrierBuffer();
1315 
1316         ASSERT(local_buffer != nullptr);
1317         LOG_DEBUG_GC << "OnThreadTerminate push queue: " << local_buffer;
1318         // we need to make it under lock, because we can have situation when we made GetG1PostBarrierBuffer
1319         // -> FillRemset -> call PushCardToUpdateThread, but it's too late - thread processed all cards already
1320         auto barrier_set = static_cast<GCG1BarrierSet *>(thread->GetBarrierSet());
1321         PushCardToUpdateThread<false>(local_buffer, barrier_set);
1322         ASSERT(local_buffer->IsEmpty());
1323         thread->ResetG1PostBarrierRingBuffer();
1324         this->GetInternalAllocator()->template Delete(local_buffer);
1325     }
1326 }
1327 
1328 template <class LanguageConfig>
PreZygoteFork()1329 void G1GC<LanguageConfig>::PreZygoteFork()
1330 {
1331     GC::PreZygoteFork();
1332     if (this->GetWorkersPool() != nullptr) {
1333         auto allocator = this->GetInternalAllocator();
1334         allocator->Delete(this->GetWorkersPool());
1335         this->ClearWorkersPool();
1336     }
1337     this->DisableWorkerThreads();
1338     update_remset_thread_->DestroyThread();
1339     // don't use thread while we are in zygote
1340     update_remset_thread_->SetUpdateConcurrent(false);
1341 }
1342 
1343 template <class LanguageConfig>
PostZygoteFork()1344 void G1GC<LanguageConfig>::PostZygoteFork()
1345 {
1346     InternalAllocatorPtr allocator = this->GetInternalAllocator();
1347     this->EnableWorkerThreads();
1348     if (this->IsWorkerThreadsExist()) {
1349         auto thread_pool =
1350             allocator->template New<GCWorkersThreadPool>(allocator, this, this->GetSettings()->GCWorkersCount());
1351         ASSERT(thread_pool != nullptr);
1352         this->SetWorkersPool(thread_pool);
1353     }
1354     GC::PostZygoteFork();
1355     // use concurrent-option after zygote
1356     update_remset_thread_->SetUpdateConcurrent(this->GetSettings()->G1EnableConcurrentUpdateRemset());
1357     update_remset_thread_->CreateThread(allocator);
1358 }
1359 
1360 template <class LanguageConfig>
DrainSatb(GCAdaptiveStack * object_stack)1361 void G1GC<LanguageConfig>::DrainSatb(GCAdaptiveStack *object_stack)
1362 {
1363     ScopedTiming scoped_timing(__FUNCTION__, *this->GetTiming());
1364     // Process satb buffers of the active threads
1365     auto callback = [this, object_stack](ManagedThread *thread) {
1366         // Acquire lock here to avoid data races with the threads
1367         // which are terminating now.
1368         // Data race is happens in thread.pre_buf_. The terminating thread may
1369         // release own pre_buf_ while GC thread iterates over threads and gets theirs
1370         // pre_buf_.
1371         os::memory::LockHolder lock(satb_and_newobj_buf_lock_);
1372         auto pre_buff = thread->GetPreBuff();
1373         if (pre_buff == nullptr) {
1374             // This can happens when the thread gives us own satb_buffer but
1375             // doesn't unregister from ThreadManaged.
1376             // At this perion GC can happen and we get pre_buff null here.
1377             return true;
1378         }
1379         for (auto obj : *pre_buff) {
1380             marker_.Mark(obj);
1381             object_stack->PushToStack(RootType::SATB_BUFFER, obj);
1382         }
1383         pre_buff->clear();
1384         return true;
1385     };
1386     // NOLINTNEXTLINE(readability-braces-around-statements)
1387     if constexpr (LanguageConfig::MT_MODE == MT_MODE_MULTI) {
1388         Thread::GetCurrent()->GetVM()->GetThreadManager()->EnumerateThreads(
1389             callback, static_cast<unsigned int>(EnumerationFlag::ALL));
1390     } else if (LanguageConfig::MT_MODE == MT_MODE_SINGLE) {  // NOLINT(readability-misleading-indentation)
1391         callback(Thread::GetCurrent()->GetVM()->GetAssociatedThread());
1392     } else {
1393         UNREACHABLE();
1394     }
1395 
1396     // Process satb buffers of the terminated threads
1397     os::memory::LockHolder lock(satb_and_newobj_buf_lock_);
1398     for (auto obj_vector : satb_buff_list_) {
1399         ASSERT(obj_vector != nullptr);
1400         for (auto obj : *obj_vector) {
1401             marker_.Mark(obj);
1402             object_stack->PushToStack(RootType::SATB_BUFFER, obj);
1403         }
1404         this->GetInternalAllocator()->Delete(obj_vector);
1405     }
1406     satb_buff_list_.clear();
1407     for (auto obj : newobj_buffer_) {
1408         marker_.Mark(obj);
1409         object_stack->PushToStack(RootType::SATB_BUFFER, obj);
1410     }
1411     newobj_buffer_.clear();
1412 }
1413 
1414 template <class LanguageConfig>
WaitForUpdateRemsetThread()1415 void G1GC<LanguageConfig>::WaitForUpdateRemsetThread()
1416 {
1417     ScopedTiming scoped_timing(__FUNCTION__, *this->GetTiming());
1418     LOG_DEBUG_GC << "Execute WaitForUpdateRemsetThread";
1419 
1420     // we forced to call EnumerateThreads many times because it takes WriteLock and we block update_remset_thread
1421     // can be done only once if EnumerateThreads will be implemented via ReadLock
1422     update_remset_thread_->WaitUntilTasksEnd();
1423 
1424     // NOLINTNEXTLINE(readability-braces-around-statements)
1425     if constexpr (LanguageConfig::MT_MODE == MT_MODE_SINGLE) {
1426         auto thread = this->GetPandaVm()->GetAssociatedThread();
1427         auto local_buffer = thread->GetG1PostBarrierBuffer();
1428         if (local_buffer == nullptr) {
1429             return;
1430         }
1431         while (!local_buffer->IsEmpty()) {
1432         }
1433     } else if (LanguageConfig::MT_MODE == MT_MODE_MULTI) {  // NOLINT(readability-misleading-indentation)
1434         while (true) {
1435             bool have_not_empty_queue = false;
1436             Thread::GetCurrent()->GetVM()->GetThreadManager()->EnumerateThreads(
1437                 [&have_not_empty_queue](ManagedThread *thread) {
1438                     auto local_buffer = thread->GetG1PostBarrierBuffer();
1439                     if (local_buffer != nullptr && !local_buffer->IsEmpty()) {
1440                         have_not_empty_queue = true;
1441                         return false;
1442                     }
1443                     return true;
1444                 },
1445                 static_cast<unsigned int>(EnumerationFlag::ALL));
1446             if (!have_not_empty_queue) {
1447                 break;
1448             }
1449         }
1450     } else {
1451         UNREACHABLE();
1452     }
1453 }
1454 
1455 template <class LanguageConfig>
ClearSatb()1456 void G1GC<LanguageConfig>::ClearSatb()
1457 {
1458     ScopedTiming scoped_timing(__FUNCTION__, *this->GetTiming());
1459     // Acquire lock here to avoid data races with the threads
1460     // which are terminating now.
1461     // Data race is happens in thread.pre_buf_. The terminating thread may
1462     // release own pre_buf_ while GC thread iterates over threads and gets theirs
1463     // pre_buf_.
1464     os::memory::LockHolder lock(satb_and_newobj_buf_lock_);
1465     // Process satb buffers of the active threads
1466     auto thread_callback = [](ManagedThread *thread) {
1467         auto pre_buff = thread->GetPreBuff();
1468         if (pre_buff != nullptr) {
1469             pre_buff->clear();
1470         }
1471         return true;
1472     };
1473     if constexpr (LanguageConfig::MT_MODE == MT_MODE_MULTI) {  // NOLINT
1474         Thread::GetCurrent()->GetVM()->GetThreadManager()->EnumerateThreads(thread_callback);
1475     } else if (LanguageConfig::MT_MODE == MT_MODE_SINGLE) {  // NOLINT(readability-misleading-indentation)
1476         thread_callback(Thread::GetCurrent()->GetVM()->GetAssociatedThread());
1477     } else {
1478         UNREACHABLE();
1479     }
1480 
1481     // Process satb buffers of the terminated threads
1482     for (auto obj_vector : satb_buff_list_) {
1483         this->GetInternalAllocator()->Delete(obj_vector);
1484     }
1485     satb_buff_list_.clear();
1486     newobj_buffer_.clear();
1487 }
1488 
1489 template <class LanguageConfig>
1490 template <class Visitor>
VisitRemSets(const Visitor & visitor)1491 void G1GC<LanguageConfig>::VisitRemSets(const Visitor &visitor)
1492 {
1493     ScopedTiming t(__FUNCTION__, *this->GetTiming());
1494 
1495     ASSERT(unique_cards_initialized_);
1496     // Iterate over stored references to the collection set
1497     for (auto &collection : unique_refs_from_remsets_) {
1498         for (auto &entry : collection) {
1499             ObjectHeader *object = entry.GetObject();
1500             uint32_t offset = entry.GetReferenceOffset();
1501             bool is_volatile = entry.IsVolatile();
1502             visitor(object, ObjectAccessor::GetObject(object, offset), offset, is_volatile);
1503         }
1504     }
1505     // Iterate over objects which have a reference to the collection set
1506     for (auto object : unique_objects_from_remsets_) {
1507         ObjectHelpers<LanguageConfig::LANG_TYPE>::TraverseAllObjectsWithInfo(object, visitor);
1508     }
1509 }
1510 
1511 template <class LanguageConfig>
CacheRefsFromRemsets()1512 void G1GC<LanguageConfig>::CacheRefsFromRemsets()
1513 {
1514     ScopedTiming t(__FUNCTION__, *this->GetTiming());
1515     // Collect only unique objects to not proceed them more than once.
1516     ASSERT(!unique_cards_initialized_);
1517     bool use_gc_workers = this->GetSettings()->ParallelCompactingEnabled() && !this->IsFullGC();
1518     auto card_visitor = [this, use_gc_workers](CardPtr card, Region *r) {
1519         ASSERT(!r->HasFlag(IS_EDEN));
1520         // In case of mixed GC don't process remsets of the tenured regions which are in the collection set
1521         bool to_process = !r->HasFlag(IS_COLLECTION_SET) || r->HasFlag(IS_NONMOVABLE) || r->HasFlag(IS_LARGE_OBJECT);
1522         if (!to_process) {
1523             return;
1524         }
1525         if (!card->IsProcessed()) {
1526             card->SetProcessed();
1527             if (!use_gc_workers ||
1528                 !this->GetWorkersPool()->AddTask(GCWorkersTaskTypes::TASK_INIT_REFS_FROM_REMSETS, card)) {
1529                 CollectRefsFromCard(card, r, &unique_refs_from_remsets_.front());
1530             }
1531         }
1532     };
1533     for (auto region : this->collection_set_) {
1534         region->GetRemSet()->ProceedMarkedCards(card_visitor);
1535     }
1536     if (use_gc_workers) {
1537         this->GetWorkersPool()->WaitUntilTasksEnd();
1538     }
1539 #ifndef NDEBUG
1540     unique_cards_initialized_ = true;
1541 #endif  // NDEBUG
1542 }
1543 
1544 template <class LanguageConfig>
ClearRefsFromRemsetsCache()1545 void G1GC<LanguageConfig>::ClearRefsFromRemsetsCache()
1546 {
1547     for (auto &collection : unique_refs_from_remsets_) {
1548         collection.clear();
1549     }
1550     unique_objects_from_remsets_.clear();
1551 #ifndef NDEBUG
1552     unique_cards_initialized_ = false;
1553 #endif  // NDEBUG
1554 }
1555 
1556 template <class LanguageConfig>
ActualizeRemSets()1557 void G1GC<LanguageConfig>::ActualizeRemSets()
1558 {
1559     ScopedTiming t(__FUNCTION__, *this->GetTiming());
1560 
1561     // Invalidate regions from collection set in all remsets
1562     for (Region *region : collection_set_.Young()) {
1563         if (!region->HasFlag(RegionFlag::IS_PROMOTED)) {
1564             RemSet<>::template InvalidateRegion<false>(region);
1565         }
1566     }
1567     for (Region *region : collection_set_.Tenured()) {
1568         RemSet<>::template InvalidateRegion<false>(region);
1569     }
1570 }
1571 
1572 template <class LanguageConfig>
ShouldRunTenuredGC(const GCTask & task)1573 bool G1GC<LanguageConfig>::ShouldRunTenuredGC(const GCTask &task)
1574 {
1575     return this->IsOnPygoteFork() || task.reason_ == GCTaskCause::OOM_CAUSE ||
1576            task.reason_ == GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE ||
1577            task.reason_ == GCTaskCause::STARTUP_COMPLETE_CAUSE;
1578 }
1579 
1580 template <class LanguageConfig>
OnWaitForIdleFail()1581 void G1GC<LanguageConfig>::OnWaitForIdleFail()
1582 {
1583     if (this->GetGCPhase() == GCPhase::GC_PHASE_MARK) {
1584         interrupt_concurrent_flag_ = true;
1585     }
1586 }
1587 
1588 TEMPLATE_CLASS_LANGUAGE_CONFIG(G1GC);
1589 template class G1GCMarker<LANG_TYPE_STATIC, false>;
1590 template class G1GCMarker<LANG_TYPE_DYNAMIC, false>;
1591 
1592 }  // namespace panda::mem
1593