• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 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 "libpandabase/mem/space.h"
17 #include "runtime/include/language_config.h"
18 #include "runtime/include/class.h"
19 #include "runtime/include/mem/panda_string.h"
20 #include "runtime/include/panda_vm.h"
21 #include "runtime/mem/gc/card_table-inl.h"
22 #include "runtime/mem/gc/dynamic/gc_marker_dynamic-inl.h"
23 #include "runtime/mem/gc/gc.h"
24 #include "runtime/mem/gc/g1/g1-gc.h"
25 #include "runtime/mem/gc/g1/g1-helpers.h"
26 #include "runtime/mem/gc/g1/ref_cache_builder.h"
27 #include "runtime/mem/gc/g1/update_remset_task_queue.h"
28 #include "runtime/mem/gc/g1/update_remset_thread.h"
29 #include "runtime/mem/gc/workers/gc_workers_task_pool.h"
30 #include "runtime/mem/gc/generational-gc-base-inl.h"
31 #include "runtime/mem/gc/static/gc_marker_static-inl.h"
32 #include "runtime/mem/gc/reference-processor/reference_processor.h"
33 #include "runtime/mem/object_helpers-inl.h"
34 #include "runtime/mem/rem_set-inl.h"
35 #include "runtime/include/thread-inl.h"
36 #include "runtime/include/managed_thread.h"
37 #include "runtime/mem/gc/g1/ref_updater.h"
38 #include "runtime/mem/region_space.h"
39 #include "runtime/include/stack_walker-inl.h"
40 #include "runtime/mem/refstorage/global_object_storage.h"
41 #include "runtime/mem/gc/g1/g1-evacuate-regions-worker-state-inl.h"
42 #include "runtime/mem/gc/g1/g1-evacuate-regions-task.h"
43 
44 namespace ark::mem {
45 
46 /* static */
47 template <class LanguageConfig>
CalcLiveBytesMarkPreprocess(const ObjectHeader * object,BaseClass * baseKlass)48 void G1GC<LanguageConfig>::CalcLiveBytesMarkPreprocess(const ObjectHeader *object, BaseClass *baseKlass)
49 {
50     Region *region = ObjectToRegion(object);
51     size_t objectSize = GetAlignedObjectSize(object->ObjectSize<LanguageConfig::LANG_TYPE>(baseKlass));
52     region->AddLiveBytes<true>(objectSize);
53 }
54 
55 /* static */
56 template <class LanguageConfig>
CalcLiveBytesNotAtomicallyMarkPreprocess(const ObjectHeader * object,BaseClass * baseKlass)57 void G1GC<LanguageConfig>::CalcLiveBytesNotAtomicallyMarkPreprocess(const ObjectHeader *object, BaseClass *baseKlass)
58 {
59     Region *region = ObjectToRegion(object);
60     size_t objectSize = GetAlignedObjectSize(object->ObjectSize<LanguageConfig::LANG_TYPE>(baseKlass));
61     region->AddLiveBytes<false>(objectSize);
62 }
63 
64 template <class LanguageConfig>
G1GC(ObjectAllocatorBase * objectAllocator,const GCSettings & settings)65 G1GC<LanguageConfig>::G1GC(ObjectAllocatorBase *objectAllocator, const GCSettings &settings)
66     : GenerationalGC<LanguageConfig>(objectAllocator, settings),
67       marker_(this),
68       concMarker_(this),
69       mixedMarker_(this),
70       concurrentMarkingStack_(this),
71       numberOfMixedTenuredRegions_(settings.GetG1NumberOfTenuredRegionsAtMixedCollection()),
72       regionGarbageRateThreshold_(settings.G1RegionGarbageRateThreshold()),
73       g1PromotionRegionAliveRate_(settings.G1PromotionRegionAliveRate()),
74       g1TrackFreedObjects_(settings.G1TrackFreedObjects()),
75       isExplicitConcurrentGcEnabled_(settings.IsExplicitConcurrentGcEnabled()),
76       regionSizeBits_(ark::helpers::math::GetIntLog2(this->GetG1ObjectAllocator()->GetRegionSize())),
77       g1PauseTracker_(settings.GetG1GcPauseIntervalInMillis(), settings.GetG1MaxGcPauseInMillis()),
78       analytics_(ark::time::GetCurrentTimeInNanos())
79 {
80     InternalAllocatorPtr allocator = this->GetInternalAllocator();
81     this->SetType(GCType::G1_GC);
82     this->SetTLABsSupported();
83     updatedRefsQueue_ = allocator->New<GCG1BarrierSet::ThreadLocalCardQueues>();
84     auto *firstRefVector = allocator->New<RefVector>();
85     firstRefVector->reserve(MAX_REFS);
86     uniqueRefsFromRemsets_.push_back(firstRefVector);
87     GetG1ObjectAllocator()->ReserveRegionIfNeeded();
88 }
89 
90 template <class LanguageConfig>
~G1GC()91 G1GC<LanguageConfig>::~G1GC()
92 {
93     InternalAllocatorPtr allocator = this->GetInternalAllocator();
94     {
95         for (auto objVector : satbBuffList_) {
96             allocator->Delete(objVector);
97         }
98     }
99     allocator->Delete(updatedRefsQueue_);
100     ASSERT(uniqueRefsFromRemsets_.size() == 1);
101     allocator->Delete(uniqueRefsFromRemsets_.front());
102     uniqueRefsFromRemsets_.clear();
103     this->GetInternalAllocator()->Delete(updateRemsetWorker_);
104 }
105 
106 template <class LanguageConfig>
InitGCBits(ark::ObjectHeader * objHeader)107 void G1GC<LanguageConfig>::InitGCBits(ark::ObjectHeader *objHeader)
108 {
109     // The mutator may create a new object during concurrent marking phase.
110     // In this case GC may don't mark it (for example only vregs may contain reference to the new object)
111     // and collect. To avoid such situations add objects to a special buffer which
112     // will be processed at remark stage.
113     if (this->GetCardTable()->GetCardPtr(ToUintPtr(objHeader))->IsYoung() ||
114         // Atomic with acquire order reason: read variable modified in GC thread
115         !concurrentMarkingFlag_.load(std::memory_order_acquire)) {
116         return;
117     }
118     os::memory::LockHolder lock(satbAndNewobjBufLock_);
119     newobjBuffer_.push_back(objHeader);
120 }
121 
122 template <class LanguageConfig>
PreStartupImp()123 void G1GC<LanguageConfig>::PreStartupImp()
124 {
125     GenerationalGC<LanguageConfig>::DisableTenuredGC();
126 }
127 
128 template <class LanguageConfig>
129 template <RegionFlag REGION_TYPE, bool FULL_GC>
DoRegionCompacting(Region * region,bool useGcWorkers,PandaVector<PandaVector<ObjectHeader * > * > * movedObjectsVector)130 void G1GC<LanguageConfig>::DoRegionCompacting(Region *region, bool useGcWorkers,
131                                               PandaVector<PandaVector<ObjectHeader *> *> *movedObjectsVector)
132 {
133     auto internalAllocator = this->GetInternalAllocator();
134     ObjectVisitor movedObjectSaver;
135     if constexpr (FULL_GC) {
136         PandaVector<ObjectHeader *> *movedObjects;
137         if (useGcWorkers) {
138             movedObjects = internalAllocator->template New<PandaVector<ObjectHeader *>>();
139             movedObjectsVector->push_back(movedObjects);
140             size_t moveSize = region->GetAllocatedBytes();
141             movedObjects->reserve(moveSize / GetMinimalObjectSize());
142         } else {
143             ASSERT(movedObjectsVector->size() == 1);
144             movedObjects = movedObjectsVector->back();
145         }
146         movedObjectSaver = [movedObjects](ObjectHeader *object) { movedObjects->push_back(object); };
147     } else {
148         movedObjectSaver = []([[maybe_unused]] const ObjectHeader *object) {};
149     }
150 
151     if (useGcWorkers) {
152         auto *storage =
153             internalAllocator->template New<GCRegionCompactWorkersTask::RegionDataType>(region, movedObjectSaver);
154         if (!this->GetWorkersTaskPool()->AddTask(GCRegionCompactWorkersTask(storage))) {
155             // We couldn't send a task to workers. Therefore, do it here.
156             internalAllocator->Delete(storage);
157             RegionCompactingImpl<true, REGION_TYPE>(region, movedObjectSaver);
158         }
159     } else {
160         RegionCompactingImpl<false, REGION_TYPE>(region, movedObjectSaver);
161     }
162 }
163 
164 class ScopedRegionCollectionInfo {
165 public:
ScopedRegionCollectionInfo(const GC * gc,const char * title,const Region * region,bool isYoung,const size_t & movedSize)166     ScopedRegionCollectionInfo(const GC *gc, const char *title, const Region *region, bool isYoung,
167                                const size_t &movedSize)
168         : gc_(gc),
169           title_(title),
170           region_(region),
171           isYoung_(isYoung),
172           movedSize_(movedSize),
173           startTimeNs_(time::GetCurrentTimeInNanos())
174     {
175     }
176 
177     NO_COPY_SEMANTIC(ScopedRegionCollectionInfo);
178     NO_MOVE_SEMANTIC(ScopedRegionCollectionInfo);
179 
~ScopedRegionCollectionInfo()180     ~ScopedRegionCollectionInfo()
181     {
182         if (gc_->IsLogDetailedGcCompactionInfoEnabled()) {
183             LOG(INFO, GC) << *this;
184         }
185     }
186 
187 private:
188     const GC *gc_;
189     const char *title_;
190     const Region *region_;
191     bool isYoung_;
192     const size_t &movedSize_;
193     uint64_t startTimeNs_;
194 
operator <<(std::ostream & log,const ScopedRegionCollectionInfo & regionInfo)195     friend std::ostream &operator<<(std::ostream &log, const ScopedRegionCollectionInfo &regionInfo)
196     {
197         auto region = regionInfo.region_;
198         log << '[' << regionInfo.gc_->GetCounter() << "] " << regionInfo.title_ << ": ";
199         // Need to use saved is_young_ flag since region flags can be changed during region promotion
200         if (regionInfo.isYoung_) {
201             log << 'Y';
202         } else {
203             log << 'T';
204         }
205         DumpRegionRange(log, *region) << " A " << ark::helpers::MemoryConverter(region->GetAllocatedBytes()) << " L ";
206         if (regionInfo.isYoung_) {
207             log << '-';
208         } else {
209             log << ark::helpers::MemoryConverter(region->GetLiveBytes());
210         }
211         log << " RS " << region->GetRemSetSize() << " M " << ark::helpers::MemoryConverter(regionInfo.movedSize_)
212             << " D " << ark::helpers::TimeConverter(time::GetCurrentTimeInNanos() - regionInfo.startTimeNs_);
213         return log;
214     }
215 };
216 
217 template <class LanguageConfig>
218 template <bool ATOMIC>
RegionPromotionImpl(Region * region,const ObjectVisitor & movedObjectSaver)219 void G1GC<LanguageConfig>::RegionPromotionImpl(Region *region, const ObjectVisitor &movedObjectSaver)
220 {
221     size_t moveSize = region->GetAllocatedBytes();
222     size_t aliveMoveCount = 0;
223     size_t deadMoveCount = 0;
224     auto objectAllocator = this->GetG1ObjectAllocator();
225     auto promotionMoveChecker = [&aliveMoveCount, &movedObjectSaver](ObjectHeader *src) {
226         ++aliveMoveCount;
227         LOG_DEBUG_OBJECT_EVENTS << "PROMOTE YOUNG object " << src;
228         ASSERT(ObjectToRegion(src)->HasFlag(RegionFlag::IS_EDEN));
229         movedObjectSaver(src);
230     };
231     auto promotionDeathChecker = [this, &deadMoveCount](ObjectHeader *objectHeader) {
232         if (IsMarked(objectHeader)) {
233             return ObjectStatus::ALIVE_OBJECT;
234         }
235         ++deadMoveCount;
236         LOG_DEBUG_OBJECT_EVENTS << "PROMOTE DEAD YOUNG object " << objectHeader;
237         return ObjectStatus::DEAD_OBJECT;
238     };
239     ScopedRegionCollectionInfo collectionInfo(this, "Region promoted", region, true, moveSize);
240     if (g1TrackFreedObjects_) {
241         // We want to track all moved objects (including), therefore, iterate over all objects in region.
242         objectAllocator->template PromoteYoungRegion<false>(region, promotionDeathChecker, promotionMoveChecker);
243     } else {
244         objectAllocator->template PromoteYoungRegion<true>(region, promotionDeathChecker, promotionMoveChecker);
245         ASSERT(deadMoveCount == 0);
246     }
247     region->RmvFlag(RegionFlag::IS_COLLECTION_SET);
248     this->memStats_.template RecordSizeMovedYoung<ATOMIC>(moveSize);
249     this->memStats_.template RecordCountMovedYoung<ATOMIC>(aliveMoveCount + deadMoveCount);
250     analytics_.ReportPromotedRegion();
251     analytics_.ReportLiveObjects(aliveMoveCount);
252 }
253 
254 template <class LanguageConfig>
255 template <typename Handler>
IterateOverRefsInMemRange(const MemRange & memRange,Region * region,Handler & refsHandler)256 void G1GC<LanguageConfig>::IterateOverRefsInMemRange(const MemRange &memRange, Region *region, Handler &refsHandler)
257 {
258     MarkBitmap *bitmap = nullptr;
259     if (region->IsEden()) {
260         ASSERT(this->IsFullGC());
261         bitmap = region->GetMarkBitmap();
262     } else {
263         bitmap = region->GetLiveBitmap();
264     }
265     auto *startAddress = ToVoidPtr(memRange.GetStartAddress());
266     auto *endAddress = ToVoidPtr(memRange.GetEndAddress());
267     auto visitor = [&refsHandler, startAddress, endAddress](void *mem) {
268         ObjectHelpers<LanguageConfig::LANG_TYPE>::template TraverseAllObjectsWithInfo<false>(
269             static_cast<ObjectHeader *>(mem), refsHandler, startAddress, endAddress);
270     };
271     if (region->HasFlag(RegionFlag::IS_LARGE_OBJECT)) {
272         bitmap->CallForMarkedChunkInHumongousRegion<false>(ToVoidPtr(region->Begin()), visitor);
273     } else {
274         bitmap->IterateOverMarkedChunkInRange(startAddress, endAddress, visitor);
275     }
276 }
277 
278 template <class LanguageConfig, bool CONCURRENTLY, bool COLLECT_CLASSES>
279 class NonRegularObjectsDeathChecker {
280 public:
NonRegularObjectsDeathChecker(size_t * deleteSize,size_t * deleteCount)281     NonRegularObjectsDeathChecker(size_t *deleteSize, size_t *deleteCount)
282         : deleteSize_(deleteSize), deleteCount_(deleteCount)
283     {
284     }
285 
286     ~NonRegularObjectsDeathChecker() = default;
287 
operator ()(ObjectHeader * objectHeader)288     ObjectStatus operator()(ObjectHeader *objectHeader)
289     {
290         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
291         if constexpr (CONCURRENTLY) {
292             // We may face a newly created object without live bitmap initialization.
293             if (objectHeader->template ClassAddr<BaseClass>() == nullptr) {
294                 return ObjectStatus::ALIVE_OBJECT;
295             }
296         }
297         Region *region = ObjectToRegion(objectHeader);
298         auto liveBitmap = region->GetLiveBitmap();
299         if (liveBitmap->AtomicTest(objectHeader)) {
300             return ObjectStatus::ALIVE_OBJECT;
301         }
302         if constexpr (!COLLECT_CLASSES) {
303             if (ObjectHelpers<LanguageConfig::LANG_TYPE>::IsClassObject(objectHeader)) {
304                 LOG_DEBUG_OBJECT_EVENTS << "DELETE NON MOVABLE class object " << objectHeader
305                                         << " but don't free memory";
306                 return ObjectStatus::ALIVE_OBJECT;
307             }
308         }
309 
310         if (region->HasFlag(RegionFlag::IS_LARGE_OBJECT)) {
311             LOG_DEBUG_OBJECT_EVENTS << "DELETE HUMONGOUS object " << objectHeader;
312             // humongous allocator increases size by region size
313             *deleteSize_ += region->Size();
314             ++(*deleteCount_);
315         } else {
316             ASSERT(region->HasFlag(RegionFlag::IS_NONMOVABLE));
317             LOG_DEBUG_OBJECT_EVENTS << "DELETE NON MOVABLE object " << objectHeader;
318         }
319         return ObjectStatus::DEAD_OBJECT;
320     }
321 
322     DEFAULT_COPY_SEMANTIC(NonRegularObjectsDeathChecker);
323     DEFAULT_MOVE_SEMANTIC(NonRegularObjectsDeathChecker);
324 
325 private:
326     size_t *deleteSize_;
327     size_t *deleteCount_;
328 };
329 
330 template <class LanguageConfig>
331 template <bool ATOMIC, bool CONCURRENTLY>
CollectEmptyRegions(GCTask & task,PandaVector<Region * > * emptyTenuredRegions)332 void G1GC<LanguageConfig>::CollectEmptyRegions(GCTask &task, PandaVector<Region *> *emptyTenuredRegions)
333 {
334     ScopedTiming t(__FUNCTION__, *this->GetTiming());
335     CollectNonRegularObjects<ATOMIC, CONCURRENTLY>();
336     ClearEmptyTenuredMovableRegions<ATOMIC, CONCURRENTLY>(emptyTenuredRegions);
337     task.UpdateGCCollectionType(GCCollectionType::TENURED);
338 }
339 
340 template <class LanguageConfig>
341 template <bool ATOMIC, bool CONCURRENTLY>
CollectNonRegularObjects()342 void G1GC<LanguageConfig>::CollectNonRegularObjects()
343 {
344     ScopedTiming t(__FUNCTION__, *this->GetTiming());
345     size_t deleteSize = 0;
346     size_t deleteCount = 0;
347     // Don't collect classes if --g1-track-free-objects is enabled.
348     // We need to know size of objects while iterating over all objects in the collected region.
349     auto deathChecker =
350         g1TrackFreedObjects_
351             ? GCObjectVisitor(
352                   NonRegularObjectsDeathChecker<LanguageConfig, CONCURRENTLY, false>(&deleteSize, &deleteCount))
353             : GCObjectVisitor(
354                   NonRegularObjectsDeathChecker<LanguageConfig, CONCURRENTLY, true>(&deleteSize, &deleteCount));
355     auto regionVisitor = [this](PandaVector<Region *> &regions) {
356         if constexpr (CONCURRENTLY) {
357             updateRemsetWorker_->InvalidateRegions(&regions);
358         } else {
359             updateRemsetWorker_->GCInvalidateRegions(&regions);
360         }
361     };
362     this->GetG1ObjectAllocator()->CollectNonRegularRegions(regionVisitor, deathChecker);
363     this->memStats_.template RecordCountFreedTenured<ATOMIC>(deleteCount);
364     this->memStats_.template RecordSizeFreedTenured<ATOMIC>(deleteSize);
365 }
366 
GetEmptyTenuredRegularRegionsFromQueue(PandaPriorityQueue<std::pair<uint32_t,Region * >> garbageRegions)367 PandaVector<Region *> GetEmptyTenuredRegularRegionsFromQueue(
368     PandaPriorityQueue<std::pair<uint32_t, Region *>> garbageRegions)
369 {
370     PandaVector<Region *> emptyTenuredRegions;
371     while (!garbageRegions.empty()) {
372         auto *topRegion = garbageRegions.top().second;
373         if (topRegion->GetLiveBytes() == 0U) {
374             emptyTenuredRegions.push_back(topRegion);
375         }
376         garbageRegions.pop();
377     }
378     return emptyTenuredRegions;
379 }
380 
381 template <class LanguageConfig>
382 template <bool ATOMIC, bool CONCURRENTLY>
ClearEmptyTenuredMovableRegions(PandaVector<Region * > * emptyTenuredRegions)383 void G1GC<LanguageConfig>::ClearEmptyTenuredMovableRegions(PandaVector<Region *> *emptyTenuredRegions)
384 {
385     ScopedTiming t(__FUNCTION__, *this->GetTiming());
386     {
387         ScopedTiming t1("Region Invalidation", *this->GetTiming());
388         if constexpr (CONCURRENTLY) {
389             updateRemsetWorker_->InvalidateRegions(emptyTenuredRegions);
390         } else {
391             updateRemsetWorker_->GCInvalidateRegions(emptyTenuredRegions);
392         }
393     }
394     size_t deleteSize = 0;
395     size_t deleteCount = 0;
396     auto deathVisitor = [](ObjectHeader *objectHeader) {
397         LOG_DEBUG_OBJECT_EVENTS << "DELETE tenured object " << objectHeader;
398     };
399     for (auto i : *emptyTenuredRegions) {
400         deleteCount += i->GetAllocatedObjects();
401         deleteSize += i->GetAllocatedBytes();
402         ASSERT(i->GetLiveBitmap()->FindFirstMarkedChunks() == nullptr);
403         if (g1TrackFreedObjects_) {
404             i->IterateOverObjects(deathVisitor);
405         }
406     }
407     {
408         ScopedTiming t2("Reset regions", *this->GetTiming());
409         if (CONCURRENTLY) {
410             this->GetG1ObjectAllocator()
411                 ->template ResetRegions<RegionFlag::IS_OLD, RegionSpace::ReleaseRegionsPolicy::NoRelease,
412                                         OSPagesPolicy::IMMEDIATE_RETURN, true, PandaVector<Region *>>(
413                     *emptyTenuredRegions);
414         } else {
415             this->GetG1ObjectAllocator()
416                 ->template ResetRegions<RegionFlag::IS_OLD, RegionSpace::ReleaseRegionsPolicy::Release,
417                                         OSPagesPolicy::NO_RETURN, false, PandaVector<Region *>>(*emptyTenuredRegions);
418         }
419     }
420     this->memStats_.template RecordCountFreedTenured<ATOMIC>(deleteCount);
421     this->memStats_.template RecordSizeFreedTenured<ATOMIC>(deleteSize);
422 }
423 
424 template <class LanguageConfig>
NeedToPromote(const Region * region) const425 bool G1GC<LanguageConfig>::NeedToPromote(const Region *region) const
426 {
427     ASSERT(region->HasFlag(RegionFlag::IS_EDEN));
428     if (region->HasPinnedObjects()) {
429         return true;
430     }
431     if ((g1PromotionRegionAliveRate_ < PERCENT_100_D) && !this->IsFullGC()) {
432         size_t aliveBytes = region->GetLiveBytes();
433         double alivePercentage = static_cast<double>(aliveBytes) / region->Size() * PERCENT_100_D;
434         if (alivePercentage >= g1PromotionRegionAliveRate_) {
435             return true;
436         }
437     }
438     return false;
439 }
440 
441 template <class LanguageConfig>
442 template <bool ATOMIC, RegionFlag REGION_TYPE>
RegionCompactingImpl(Region * region,const ObjectVisitor & movedObjectSaver)443 void G1GC<LanguageConfig>::RegionCompactingImpl(Region *region, const ObjectVisitor &movedObjectSaver)
444 {
445     auto objectAllocator = this->GetG1ObjectAllocator();
446     // Calculated live bytes in region for all marked objects during MixedMark
447     size_t moveSize = region->GetLiveBytes();
448     size_t moveCount = 0;
449     size_t allocatedSize = region->GetAllocatedBytes();
450     ASSERT(moveSize <= allocatedSize);
451     size_t deleteSize = allocatedSize - moveSize;
452     size_t deleteCount = 0;
453 
454     auto moveChecker = [this, &moveCount, &movedObjectSaver](ObjectHeader *src, ObjectHeader *dst) {
455         LOG_DEBUG_OBJECT_EVENTS << "MOVE object " << src << " -> " << dst;
456         ASSERT(ObjectToRegion(dst)->HasFlag(RegionFlag::IS_OLD));
457         this->SetForwardAddress(src, dst);
458         ++moveCount;
459         movedObjectSaver(dst);
460     };
461 
462     auto deathChecker = [this, &deleteCount](ObjectHeader *objectHeader) {
463         if (IsMarked(objectHeader)) {
464             return ObjectStatus::ALIVE_OBJECT;
465         }
466         ++deleteCount;
467         if constexpr (REGION_TYPE == RegionFlag::IS_EDEN) {
468             LOG_DEBUG_OBJECT_EVENTS << "DELETE YOUNG object " << objectHeader;
469         } else {
470             ASSERT(REGION_TYPE == RegionFlag::IS_OLD);
471             LOG_DEBUG_OBJECT_EVENTS << "DELETE TENURED object " << objectHeader;
472         }
473         return ObjectStatus::DEAD_OBJECT;
474     };
475     if constexpr (REGION_TYPE == RegionFlag::IS_EDEN) {
476         if (!this->NeedToPromote(region)) {
477             ScopedRegionCollectionInfo collectionInfo(this, "Region compacted", region, true, moveSize);
478             if (g1TrackFreedObjects_) {
479                 // We want to track all freed objects, therefore, iterate over all objects in region.
480                 objectAllocator->template CompactRegion<RegionFlag::IS_EDEN, false>(region, deathChecker, moveChecker);
481             } else {
482                 objectAllocator->template CompactRegion<RegionFlag::IS_EDEN, true>(region, deathChecker, moveChecker);
483                 // delete_count is equal to 0 because we don't track allocation in TLABs by a default.
484                 // We will do it only with PANDA_TRACK_TLAB_ALLOCATIONS key
485                 ASSERT(deleteCount == 0);
486             }
487             this->memStats_.template RecordSizeMovedYoung<ATOMIC>(moveSize);
488             this->memStats_.template RecordCountMovedYoung<ATOMIC>(moveCount);
489             this->memStats_.template RecordSizeFreedYoung<ATOMIC>(deleteSize);
490             this->memStats_.template RecordCountFreedYoung<ATOMIC>(deleteCount);
491             analytics_.ReportEvacuatedBytes(moveSize);
492             analytics_.ReportLiveObjects(moveCount);
493         } else {
494             RegionPromotionImpl<ATOMIC>(region, movedObjectSaver);
495         }
496     } else {
497         ScopedRegionCollectionInfo collectionInfo(this, "Region compacted", region, false, moveSize);
498         ASSERT(region->HasFlag(RegionFlag::IS_OLD));
499         ASSERT(!region->HasFlag(RegionFlag::IS_NONMOVABLE) && !region->HasFlag(RegionFlag::IS_LARGE_OBJECT));
500         if (g1TrackFreedObjects_) {
501             // We want to track all freed objects, therefore, iterate over all objects in region.
502             objectAllocator->template CompactRegion<RegionFlag::IS_OLD, false>(region, deathChecker, moveChecker);
503         } else {
504             objectAllocator->template CompactRegion<RegionFlag::IS_OLD, true>(region, deathChecker, moveChecker);
505             size_t allocatedObjects = region->GetAllocatedObjects();
506             ASSERT(moveCount <= allocatedObjects);
507             ASSERT(deleteCount == 0);
508             deleteCount = allocatedObjects - moveCount;
509         }
510         this->memStats_.template RecordSizeMovedTenured<ATOMIC>(moveSize);
511         this->memStats_.template RecordCountMovedTenured<ATOMIC>(moveCount);
512         this->memStats_.template RecordSizeFreedTenured<ATOMIC>(deleteSize);
513         this->memStats_.template RecordCountFreedTenured<ATOMIC>(deleteCount);
514     }
515 }
516 
517 template <class LanguageConfig, typename RefUpdater, bool FULL_GC>
DoUpdateReferencesToMovedObjectsRange(typename GCUpdateRefsWorkersTask<FULL_GC>::MovedObjectsRange * movedObjects,RefUpdater & refUpdater)518 void DoUpdateReferencesToMovedObjectsRange(typename GCUpdateRefsWorkersTask<FULL_GC>::MovedObjectsRange *movedObjects,
519                                            RefUpdater &refUpdater)
520 {
521     for (auto *obj : *movedObjects) {
522         if constexpr (!FULL_GC) {
523             obj = obj->IsForwarded() ? GetForwardAddress(obj) : obj;
524         }
525         ObjectHelpers<LanguageConfig::LANG_TYPE>::template TraverseAllObjectsWithInfo<false>(obj, refUpdater);
526     }
527 }
528 
529 template <class LanguageConfig>
WorkerTaskProcessing(GCWorkersTask * task,void * workerData)530 void G1GC<LanguageConfig>::WorkerTaskProcessing(GCWorkersTask *task, [[maybe_unused]] void *workerData)
531 {
532     switch (task->GetType()) {
533         case GCWorkersTaskTypes::TASK_MARKING: {
534             auto objectsStack = task->Cast<GCMarkWorkersTask>()->GetMarkingStack();
535             MarkStackMixed(objectsStack);
536             ASSERT(objectsStack->Empty());
537             this->GetInternalAllocator()->Delete(objectsStack);
538             break;
539         }
540         case GCWorkersTaskTypes::TASK_REMARK: {
541             auto *objectsStack = task->Cast<GCMarkWorkersTask>()->GetMarkingStack();
542             this->MarkStack(&marker_, objectsStack, CalcLiveBytesMarkPreprocess);
543             ASSERT(objectsStack->Empty());
544             this->GetInternalAllocator()->Delete(objectsStack);
545             break;
546         }
547         case GCWorkersTaskTypes::TASK_FULL_MARK: {
548             const ReferenceCheckPredicateT &refEnablePred = []([[maybe_unused]] const ObjectHeader *obj) {
549                 // process all refs
550                 return true;
551             };
552             auto *objectsStack = task->Cast<GCMarkWorkersTask>()->GetMarkingStack();
553             this->MarkStack(&marker_, objectsStack, CalcLiveBytesMarkPreprocess, refEnablePred);
554             ASSERT(objectsStack->Empty());
555             this->GetInternalAllocator()->Delete(objectsStack);
556             break;
557         }
558         case GCWorkersTaskTypes::TASK_REGION_COMPACTING: {
559             auto *data = task->Cast<GCRegionCompactWorkersTask>()->GetRegionData();
560             Region *region = data->first;
561             const ObjectVisitor &movedObjectsSaver = data->second;
562             if (region->HasFlag(RegionFlag::IS_EDEN)) {
563                 RegionCompactingImpl<true, RegionFlag::IS_EDEN>(region, movedObjectsSaver);
564             } else if (region->HasFlag(RegionFlag::IS_OLD)) {
565                 RegionCompactingImpl<true, RegionFlag::IS_OLD>(region, movedObjectsSaver);
566             } else {
567                 LOG(FATAL, GC) << "Unsupported region type";
568             }
569             this->GetInternalAllocator()->Delete(data);
570             break;
571         }
572         case GCWorkersTaskTypes::TASK_RETURN_FREE_PAGES_TO_OS: {
573             auto wasInterrupted =
574                 PoolManager::GetMmapMemPool()->ReleaseFreePagesToOSWithInterruption(releasePagesInterruptFlag_);
575             releasePagesInterruptFlag_ =
576                 wasInterrupted ? ReleasePagesStatus::WAS_INTERRUPTED : ReleasePagesStatus::FINISHED;
577             break;
578         }
579         case GCWorkersTaskTypes::TASK_ENQUEUE_REMSET_REFS: {
580             auto *movedObjectsRange = task->Cast<GCUpdateRefsWorkersTask<false>>()->GetMovedObjectsRange();
581             auto *taskUpdatedRefsQueue =
582                 this->GetInternalAllocator()->template New<GCG1BarrierSet::ThreadLocalCardQueues>();
583             EnqueueRemsetRefUpdater<LanguageConfig> refUpdater(this->GetCardTable(), taskUpdatedRefsQueue,
584                                                                regionSizeBits_);
585             DoUpdateReferencesToMovedObjectsRange<LanguageConfig, decltype(refUpdater), false>(movedObjectsRange,
586                                                                                                refUpdater);
587             {
588                 os::memory::LockHolder lock(gcWorkerQueueLock_);
589                 updatedRefsQueue_->insert(updatedRefsQueue_->end(), taskUpdatedRefsQueue->begin(),
590                                           taskUpdatedRefsQueue->end());
591             }
592             this->GetInternalAllocator()->Delete(movedObjectsRange);
593             this->GetInternalAllocator()->Delete(taskUpdatedRefsQueue);
594             break;
595         }
596         case GCWorkersTaskTypes::TASK_EVACUATE_REGIONS: {
597             auto *evacuationTask = task->Cast<G1EvacuateRegionsTask<LanguageConfig>>();
598             evacuationTask->Work();
599             break;
600         }
601         default:
602             LOG(FATAL, GC) << "Unimplemented for " << GCWorkersTaskTypesToString(task->GetType());
603             UNREACHABLE();
604     }
605 }
606 
607 template <class LanguageConfig>
UpdateCollectionSet(const CollectionSet & collectibleRegions)608 void G1GC<LanguageConfig>::UpdateCollectionSet(const CollectionSet &collectibleRegions)
609 {
610     collectionSet_ = collectibleRegions;
611     for (auto r : collectionSet_) {
612         // we don't need to reset flag, because we don't reuse collectionSet region
613         r->AddFlag(RegionFlag::IS_COLLECTION_SET);
614         LOG_DEBUG_GC << "dump region: " << *r;
615     }
616 }
617 
618 template <class LanguageConfig>
RunPhasesForRegions(ark::GCTask & task,const CollectionSet & collectibleRegions)619 void G1GC<LanguageConfig>::RunPhasesForRegions(ark::GCTask &task, const CollectionSet &collectibleRegions)
620 {
621     if (collectibleRegions.empty()) {
622         LOG_DEBUG_GC << "No regions specified for collection " << task.reason;
623     }
624     ASSERT(concurrentMarkingStack_.Empty());
625     this->GetObjectGenAllocator()->InvalidateSpaceData();
626     this->GetObjectGenAllocator()->UpdateSpaceData();
627     RunGC(task, collectibleRegions);
628 }
629 
630 template <class LanguageConfig>
NeedToRunGC(const ark::GCTask & task)631 bool G1GC<LanguageConfig>::NeedToRunGC(const ark::GCTask &task)
632 {
633     return (task.reason == GCTaskCause::YOUNG_GC_CAUSE) || (task.reason == GCTaskCause::OOM_CAUSE) ||
634            (task.reason == GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE) ||
635            (task.reason == GCTaskCause::STARTUP_COMPLETE_CAUSE) || (task.reason == GCTaskCause::EXPLICIT_CAUSE) ||
636            (task.reason == GCTaskCause::NATIVE_ALLOC_CAUSE) || (task.reason == GCTaskCause::MIXED);
637 }
638 
639 template <class LanguageConfig>
NeedFullGC(const ark::GCTask & task)640 bool G1GC<LanguageConfig>::NeedFullGC(const ark::GCTask &task)
641 {
642     return this->IsExplicitFull(task) || (task.reason == GCTaskCause::OOM_CAUSE);
643 }
644 
645 template <class LanguageConfig>
RunPhasesImpl(ark::GCTask & task)646 void G1GC<LanguageConfig>::RunPhasesImpl(ark::GCTask &task)
647 {
648     SuspendUpdateRemsetWorkerScope stopUpdateRemsetWorkerScope(updateRemsetWorker_);
649     interruptConcurrentFlag_ = false;
650     LOG_DEBUG_GC << "G1GC start, reason: " << task.reason;
651     LOG_DEBUG_GC << "Footprint before GC: " << this->GetPandaVm()->GetMemStats()->GetFootprintHeap();
652     task.UpdateGCCollectionType(GCCollectionType::YOUNG);
653 
654     InterruptReleasePagesIfNeeded();
655 
656     size_t bytesInHeapBeforeMove = this->GetPandaVm()->GetMemStats()->GetFootprintHeap();
657     {
658         ScopedTiming t("G1 GC", *this->GetTiming());
659         auto startCollectionTime = ark::time::GetCurrentTimeInNanos();
660         analytics_.ReportCollectionStart(startCollectionTime);
661         {
662             GCScopedPauseStats scopedPauseStats(this->GetPandaVm()->GetGCStats());
663             this->memStats_.Reset();
664             if (NeedToRunGC(task)) {
665                 // Check there is no concurrent mark running by another thread.
666                 EnsurePreWrbDisabledInThreads();
667 
668                 if (NeedFullGC(task)) {
669                     task.collectionType = GCCollectionType::FULL;
670                     RunFullGC(task);
671                 } else {
672                     TryRunMixedGC(task);
673                 }
674             }
675         }
676 
677         if (this->GetSettings()->G1EnablePauseTimeGoal()) {
678             auto endCollectionTime = ark::time::GetCurrentTimeInNanos();
679             g1PauseTracker_.AddPauseInNanos(startCollectionTime, endCollectionTime);
680             analytics_.ReportCollectionEnd(task.reason, endCollectionTime, collectionSet_, true);
681         }
682         collectionSet_.clear();
683 
684         if (task.reason == GCTaskCause::MIXED) {
685             // There was forced a mixed GC. This GC type sets specific settings.
686             // So we need to restore them.
687             regionGarbageRateThreshold_ = this->GetSettings()->G1RegionGarbageRateThreshold();
688         }
689         if (ScheduleMixedGCAndConcurrentMark(task)) {
690             RunConcurrentMark(task);
691         }
692     }
693     // Update global and GC memstats based on generational memstats information
694     // We will update tenured stats and record allocations, so set 'true' values
695     this->UpdateMemStats(bytesInHeapBeforeMove, true, true);
696 
697     StartReleasePagesIfNeeded(ReleasePagesStatus::WAS_INTERRUPTED);
698 
699     LOG_DEBUG_GC << "Footprint after GC: " << this->GetPandaVm()->GetMemStats()->GetFootprintHeap();
700     this->SetFullGC(false);
701 }
702 
703 template <class LanguageConfig>
RunFullGC(ark::GCTask & task)704 void G1GC<LanguageConfig>::RunFullGC(ark::GCTask &task)
705 {
706     ScopedTiming t("Run Full GC", *this->GetTiming());
707     GetG1ObjectAllocator()->template ReleaseEmptyRegions<RegionFlag::IS_OLD, OSPagesPolicy::NO_RETURN>();
708     LOG_DEBUG_GC << "Explicit Full GC invocation due to a reason: " << task.reason;
709     this->SetFullGC(true);
710     FullMarking(task);
711     if (!HaveEnoughRegionsToMove(1)) {
712         GetG1ObjectAllocator()->ReleaseReservedRegion();
713         // After release reserved region we always have minimum 1 region for tenured collection
714         ASSERT(HaveEnoughRegionsToMove(1));
715     }
716     CollectionSet collectionSet = GetFullCollectionSet();
717     ClearTenuredCards(collectionSet);
718     PrepareYoungRegionsForFullGC(collectionSet);
719     CollectAndMoveTenuredRegions(collectionSet);
720     // Reserve a region to prevent OOM in case a lot of garbage in tenured space
721     GetG1ObjectAllocator()->ReserveRegionIfNeeded();
722     CollectAndMoveYoungRegions(collectionSet);
723     ReleasePagesInFreePools();
724     this->SetFullGC(false);
725 }
726 
727 template <class LanguageConfig>
TryRunMixedGC(ark::GCTask & task)728 void G1GC<LanguageConfig>::TryRunMixedGC(ark::GCTask &task)
729 {
730     bool isMixed = false;
731     if (task.reason == GCTaskCause::MIXED && !interruptConcurrentFlag_) {
732         regionGarbageRateThreshold_ = 0;
733         isMixed = true;
734     } else {
735         // Atomic with acquire order reason: to see changes made by GC thread (which do concurrent
736         // marking and than set isMixedGcRequired_) in mutator thread which waits for the end of
737         // concurrent marking.
738         isMixed = isMixedGcRequired_.load(std::memory_order_acquire);
739     }
740     task.collectionType = isMixed ? GCCollectionType::MIXED : GCCollectionType::YOUNG;
741     // Handle pending dirty cards here to be able to estimate scanning time while adding old regions to
742     // collection set
743     HandlePendingDirtyCards();
744     auto collectibleRegions = GetCollectibleRegions(task, isMixed);
745     if (!collectibleRegions.empty() && HaveEnoughSpaceToMove(collectibleRegions)) {
746         // Ordinary collection flow
747         RunMixedGC(task, collectibleRegions);
748     } else if (collectibleRegions.empty()) {
749         LOG_DEBUG_GC << "Failed to run gc: nothing to collect in movable space";
750     } else {
751         // There are no space to move objects. Need to skip concurrent marking
752         // in this case, since it ignores young roots
753         // Atomic with release order reason: to see changes made by GC thread (which do concurrent
754         // marking and than set isMixedGcRequired_) in mutator thread which waits for the end of
755         // concurrent marking.
756         isMixedGcRequired_.store(true, std::memory_order_release);
757         LOG_DEBUG_GC << "Failed to run gc: not enough free regions to move";
758     }
759     ReenqueueDirtyCards();
760 }
761 
762 template <class LanguageConfig>
CollectAndMoveTenuredRegions(const CollectionSet & collectionSet)763 void G1GC<LanguageConfig>::CollectAndMoveTenuredRegions(const CollectionSet &collectionSet)
764 {
765     auto curRegionIt = collectionSet.Tenured().begin();
766     auto endRegionIt = collectionSet.Tenured().end();
767     while (curRegionIt != endRegionIt) {
768         ASSERT(HaveEnoughRegionsToMove(1));
769         CollectionSet cs;
770         while ((curRegionIt != endRegionIt) && (HaveEnoughRegionsToMove(cs.Movable().size() + 1))) {
771             Region *region = *curRegionIt;
772             curRegionIt++;
773             if (region->GetGarbageBytes() > 0) {
774                 LOG_DEBUG_GC << "Add region " << *region << " to a collection set";
775                 cs.AddRegion(region);
776                 continue;
777             }
778 
779             double regionFragmentation = region->GetFragmentation();
780             if (regionFragmentation < this->GetSettings()->G1FullGCRegionFragmentationRate()) {
781                 LOG_DEBUG_GC << "Skip region " << *region << " because it has no garbage inside";
782                 continue;
783             }
784 
785             LOG_DEBUG_GC << "Add region " << *region
786                          << " to a collection set because it has a big fragmentation = " << regionFragmentation;
787             cs.AddRegion(region);
788         }
789         UpdateCollectionSet(cs);
790         CollectAndMove<true>(cs);
791         LOG_DEBUG_GC << "Iterative full GC, collected " << cs.size() << " regions";
792     }
793 }
794 
795 template <class LanguageConfig>
CollectAndMoveYoungRegions(const CollectionSet & collectionSet)796 void G1GC<LanguageConfig>::CollectAndMoveYoungRegions(const CollectionSet &collectionSet)
797 {
798     if (!collectionSet.Young().empty()) {
799         CollectionSet cs(collectionSet.Young());
800         if (HaveEnoughRegionsToMove(cs.Movable().size())) {
801             LOG_DEBUG_GC << "Iterative full GC. Collecting " << cs.size() << " young regions";
802             UpdateCollectionSet(cs);
803             CollectAndMove<true>(cs);
804         } else {
805             RestoreYoungRegionsAfterFullGC(cs);
806             LOG_INFO_GC << "Failed to run gc, not enough free regions for young";
807             LOG_INFO_GC << "Accounted total object used bytes = "
808                         << PoolManager::GetMmapMemPool()->GetObjectUsedBytes();
809         }
810     }
811 }
812 
813 template <class LanguageConfig>
ReleasePagesInFreePools()814 void G1GC<LanguageConfig>::ReleasePagesInFreePools()
815 {
816     ScopedTiming releasePages("Release Pages in Free Pools", *this->GetTiming());
817     bool useGcWorkers = this->GetSettings()->GCWorkersCount() != 0;
818     if (useGcWorkers) {
819         StartReleasePagesIfNeeded(ReleasePagesStatus::FINISHED);
820     } else {
821         PoolManager::GetMmapMemPool()->ReleaseFreePagesToOS();
822     }
823 }
824 
825 template <class LanguageConfig>
RunMixedGC(ark::GCTask & task,const CollectionSet & collectionSet)826 void G1GC<LanguageConfig>::RunMixedGC(ark::GCTask &task, const CollectionSet &collectionSet)
827 {
828     auto startTime = ark::time::GetCurrentTimeInNanos();
829     LOG_DEBUG_GC << "Collect regions size:" << collectionSet.size();
830     UpdateCollectionSet(collectionSet);
831     RunPhasesForRegions(task, collectionSet);
832     auto endTime = ark::time::GetCurrentTimeInNanos();
833     this->GetStats()->AddTimeValue(endTime - startTime, TimeTypeStats::YOUNG_TOTAL_TIME);
834 }
835 
836 template <class LanguageConfig>
ScheduleMixedGCAndConcurrentMark(ark::GCTask & task)837 bool G1GC<LanguageConfig>::ScheduleMixedGCAndConcurrentMark(ark::GCTask &task)
838 {
839     // Atomic with acquire order reason: to see changes made by GC thread (which do concurrent marking and than set
840     // isMixedGcRequired_) in mutator thread which waits for the end of concurrent marking.
841     if (isMixedGcRequired_.load(std::memory_order_acquire)) {
842         if (!HaveGarbageRegions()) {
843             // Atomic with release order reason: to see changes made by GC thread (which do concurrent marking and
844             // than set isMixedGcRequired_) in mutator thread which waits for the end of concurrent marking.
845             isMixedGcRequired_.store(false, std::memory_order_release);
846         }
847         return false;  // don't run concurrent mark
848     }
849     concurrentMarkingFlag_ = !interruptConcurrentFlag_ && this->ShouldRunTenuredGC(task);
850     // Atomic with relaxed order reason: read variable modified in the same thread
851     return concurrentMarkingFlag_.load(std::memory_order_relaxed);
852 }
853 
854 template <class LanguageConfig>
855 template <bool ENABLE_BARRIER>
UpdatePreWrbEntrypointInThreads()856 void G1GC<LanguageConfig>::UpdatePreWrbEntrypointInThreads()
857 {
858     ObjRefProcessFunc entrypointFunc = nullptr;
859     if constexpr (ENABLE_BARRIER) {
860         auto addr = this->GetBarrierSet()->GetBarrierOperand(ark::mem::BarrierPosition::BARRIER_POSITION_PRE,
861                                                              "STORE_IN_BUFF_TO_MARK_FUNC");
862         entrypointFunc = std::get<ObjRefProcessFunc>(addr.GetValue());
863     }
864     auto setEntrypoint = [this, &entrypointFunc](ManagedThread *thread) {
865         void *entrypointFuncUntyped = reinterpret_cast<void *>(entrypointFunc);
866         ASSERT(thread->GetPreWrbEntrypoint() != entrypointFuncUntyped);
867         thread->SetPreWrbEntrypoint(entrypointFuncUntyped);
868 
869         // currentPreWrbEntrypoint_ is not required to be set multiple times, but this has to be done under the
870         // EnumerateThreads()'s lock, hence the repetition
871         currentPreWrbEntrypoint_ = entrypointFunc;
872         return true;
873     };
874     this->GetPandaVm()->GetThreadManager()->EnumerateThreads(setEntrypoint);
875 }
876 
877 template <class LanguageConfig>
EnsurePreWrbDisabledInThreads()878 void G1GC<LanguageConfig>::EnsurePreWrbDisabledInThreads()
879 {
880     [[maybe_unused]] auto callback = [](ManagedThread *thread) { return thread->GetPreWrbEntrypoint() == nullptr; };
881     ASSERT(this->GetPandaVm()->GetThreadManager()->EnumerateThreads(callback));
882 }
883 
884 template <class LanguageConfig>
RunConcurrentMark(ark::GCTask & task)885 void G1GC<LanguageConfig>::RunConcurrentMark(ark::GCTask &task)
886 {
887     ASSERT(collectionSet_.empty());
888     // Init concurrent marking
889     EnablePreWrbInThreads();
890 
891     if (this->GetSettings()->BeforeG1ConcurrentHeapVerification()) {
892         trace::ScopedTrace postHeapVerifierTrace("PostGCHeapVeriFier before concurrent");
893         size_t failCount = this->VerifyHeap();
894         if (this->GetSettings()->FailOnHeapVerification() && failCount > 0) {
895             LOG(FATAL, GC) << "Heap corrupted after GC, HeapVerifier found " << failCount << " corruptions";
896         }
897     }
898     ConcurrentMarking(task);
899 }
900 
901 template <class LanguageConfig>
HaveGarbageRegions()902 bool G1GC<LanguageConfig>::HaveGarbageRegions()
903 {
904     // Use GetTopGarbageRegions because it doesn't return current regions
905     auto regions = GetG1ObjectAllocator()->template GetTopGarbageRegions<false>();
906     return HaveGarbageRegions(regions);
907 }
908 
909 template <class LanguageConfig>
GetOldCollectionSetCandidatesNumber()910 size_t G1GC<LanguageConfig>::GetOldCollectionSetCandidatesNumber()
911 {
912     auto regions = GetG1ObjectAllocator()->template GetTopGarbageRegions<false>();
913     size_t count = 0;
914     while (!regions.empty()) {
915         auto *region = regions.top().second;
916         double garbageRate = static_cast<double>(region->GetGarbageBytes()) / region->Size();
917         if (garbageRate < regionGarbageRateThreshold_) {
918             break;
919         }
920         regions.pop();
921         count++;
922     }
923     return count;
924 }
925 
926 template <class LanguageConfig>
HaveGarbageRegions(const PandaPriorityQueue<std::pair<uint32_t,Region * >> & regions)927 bool G1GC<LanguageConfig>::HaveGarbageRegions(const PandaPriorityQueue<std::pair<uint32_t, Region *>> &regions)
928 {
929     if (regions.empty()) {
930         return false;
931     }
932     auto *topRegion = regions.top().second;
933     double garbageRate = static_cast<double>(topRegion->GetGarbageBytes()) / topRegion->Size();
934     return garbageRate >= regionGarbageRateThreshold_;
935 }
936 
937 template <class LanguageConfig>
ProcessDirtyCards()938 void G1GC<LanguageConfig>::ProcessDirtyCards()
939 {
940     ScopedTiming t(__FUNCTION__, *this->GetTiming());
941     updateRemsetWorker_->GCProcessCards();
942 }
943 
944 template <class LanguageConfig>
CreateUpdateRemsetWorker()945 void G1GC<LanguageConfig>::CreateUpdateRemsetWorker()
946 {
947     InternalAllocatorPtr allocator = this->GetInternalAllocator();
948     // to make TSAN happy because we access updated_refs_queue_ inside constructor of UpdateRemsetWorker
949     os::memory::LockHolder lock(queueLock_);
950     if (this->GetSettings()->UseThreadPoolForGC()) {
951         updateRemsetWorker_ = allocator->template New<UpdateRemsetThread<LanguageConfig>>(
952             this, updatedRefsQueue_, &queueLock_, this->GetG1ObjectAllocator()->GetRegionSize(),
953             this->GetSettings()->G1EnableConcurrentUpdateRemset(), this->GetSettings()->G1MinConcurrentCardsToProcess(),
954             this->GetSettings()->G1HotCardsProcessingFrequency());
955     } else {
956         ASSERT(this->GetSettings()->UseTaskManagerForGC());
957         updateRemsetWorker_ = allocator->template New<UpdateRemsetTaskQueue<LanguageConfig>>(
958             this, updatedRefsQueue_, &queueLock_, this->GetG1ObjectAllocator()->GetRegionSize(),
959             this->GetSettings()->G1EnableConcurrentUpdateRemset(), this->GetSettings()->G1MinConcurrentCardsToProcess(),
960             this->GetSettings()->G1HotCardsProcessingFrequency());
961     }
962     ASSERT(updateRemsetWorker_ != nullptr);
963 }
964 
965 template <class LanguageConfig>
InitializeImpl()966 void G1GC<LanguageConfig>::InitializeImpl()
967 {
968     // GC saved the PandaVM instance, so we get allocator from the PandaVM.
969     InternalAllocatorPtr allocator = this->GetInternalAllocator();
970     this->CreateCardTable(allocator, PoolManager::GetMmapMemPool()->GetMinObjectAddress(),
971                           PoolManager::GetMmapMemPool()->GetTotalObjectSize());
972 
973     auto barrierSet =
974         allocator->New<GCG1BarrierSet>(allocator, &PreWrbFuncEntrypoint, &PostWrbUpdateCardFuncEntrypoint,
975                                        ark::helpers::math::GetIntLog2(this->GetG1ObjectAllocator()->GetRegionSize()),
976                                        this->GetCardTable(), updatedRefsQueue_, &queueLock_);
977     ASSERT(barrierSet != nullptr);
978     this->SetGCBarrierSet(barrierSet);
979 
980     this->CreateWorkersTaskPool();
981     CreateUpdateRemsetWorker();
982     LOG_DEBUG_GC << "G1GC initialized";
983 }
984 
985 template <class LanguageConfig>
MarkObject(ObjectHeader * object)986 void G1GC<LanguageConfig>::MarkObject(ObjectHeader *object)
987 {
988     G1GCPauseMarker<LanguageConfig>::Mark(object);
989 }
990 
991 template <class LanguageConfig>
MarkObjectIfNotMarked(ObjectHeader * object)992 bool G1GC<LanguageConfig>::MarkObjectIfNotMarked(ObjectHeader *object)
993 {
994     ASSERT(object != nullptr);
995     if (this->GetGCPhase() == GCPhase::GC_PHASE_MARK_YOUNG) {
996         return mixedMarker_.MarkIfNotMarked(object);
997     }
998     return marker_.MarkIfNotMarked(object);
999 }
1000 
1001 template <class LanguageConfig>
InitGCBitsForAllocationInTLAB(ark::ObjectHeader * object)1002 void G1GC<LanguageConfig>::InitGCBitsForAllocationInTLAB([[maybe_unused]] ark::ObjectHeader *object)
1003 {
1004     LOG(FATAL, GC) << "Not implemented";
1005 }
1006 
1007 template <class LanguageConfig>
IsMarked(ark::ObjectHeader const * object) const1008 bool G1GC<LanguageConfig>::IsMarked(ark::ObjectHeader const *object) const
1009 {
1010     return G1GCPauseMarker<LanguageConfig>::IsMarked(object);
1011 }
1012 
1013 template <class LanguageConfig>
MarkStackMixed(GCMarkingStackType * stack)1014 void G1GC<LanguageConfig>::MarkStackMixed(GCMarkingStackType *stack)
1015 {
1016     ASSERT(stack != nullptr);
1017     trace::ScopedTrace scopedTrace(__FUNCTION__);
1018     auto refPred = [this](const ObjectHeader *obj) { return InGCSweepRange(obj); };
1019     auto visitor = [this, stack, &refPred](const ObjectHeader *object) {
1020         ASSERT(mixedMarker_.IsMarked(object));
1021         ValidateObject(nullptr, object);
1022         auto *objectClass = object->template ClassAddr<BaseClass>();
1023         // We need annotation here for the FullMemoryBarrier used in InitializeClassByIdEntrypoint
1024         TSAN_ANNOTATE_HAPPENS_AFTER(objectClass);
1025         LOG_DEBUG_GC << "Current object: " << GetDebugInfoAboutObject(object);
1026 
1027         ASSERT(!object->IsForwarded());
1028         ASSERT(InGCSweepRange(object));
1029         CalcLiveBytesMarkPreprocess(object, objectClass);
1030         mixedMarker_.MarkInstance(stack, object, objectClass, refPred);
1031     };
1032     {
1033         auto markedObjects = stack->TraverseObjects(visitor);
1034         os::memory::LockHolder lh(mixedMarkedObjectsMutex_);
1035         if (mixedMarkedObjects_.empty()) {
1036             mixedMarkedObjects_ = std::move(markedObjects);
1037         } else {
1038             mixedMarkedObjects_.insert(mixedMarkedObjects_.end(), markedObjects.begin(), markedObjects.end());
1039         }
1040     }
1041 }
1042 
1043 template <class LanguageConfig>
MarkStackFull(GCMarkingStackType * stack)1044 void G1GC<LanguageConfig>::MarkStackFull(GCMarkingStackType *stack)
1045 {
1046     this->MarkStack(&marker_, stack, CalcLiveBytesMarkPreprocess, GC::EmptyReferenceProcessPredicate);
1047 }
1048 
1049 template <class LanguageConfig>
MarkReferences(GCMarkingStackType * references,GCPhase gcPhase)1050 void G1GC<LanguageConfig>::MarkReferences(GCMarkingStackType *references, GCPhase gcPhase)
1051 {
1052     trace::ScopedTrace scopedTrace(__FUNCTION__);
1053     LOG_DEBUG_GC << "Start marking " << references->Size() << " references";
1054     // mark refs only on mixed-gc and on full_gc. On concurrent mark we don't handle any references
1055     if (gcPhase == GCPhase::GC_PHASE_MARK_YOUNG) {
1056         MarkStackMixed(references);
1057     } else if (this->IsFullGC()) {
1058         MarkStackFull(references);
1059     } else if (gcPhase == GCPhase::GC_PHASE_INITIAL_MARK || gcPhase == GCPhase::GC_PHASE_MARK ||
1060                gcPhase == GCPhase::GC_PHASE_REMARK) {
1061         // nothing
1062     } else {
1063         LOG_DEBUG_GC << "phase: " << GCScopedPhase::GetPhaseName(gcPhase);
1064         UNREACHABLE();
1065     }
1066 }
1067 
1068 template <class LanguageConfig>
InGCSweepRange(const ObjectHeader * object) const1069 bool G1GC<LanguageConfig>::InGCSweepRange(const ObjectHeader *object) const
1070 {
1071     ASSERT_DO(!this->collectionSet_.empty() || this->IsFullGC(),
1072               std::cerr << "Incorrect phase in InGCSweepRange: " << static_cast<size_t>(this->GetGCPhase()) << "\n");
1073     ASSERT(IsHeapSpace(PoolManager::GetMmapMemPool()->GetSpaceTypeForAddr(object)));
1074     Region *objRegion = ObjectToRegion(object);
1075     return objRegion->IsInCollectionSet();
1076 }
1077 
RemsetRegionPredicate(const Region * r)1078 static bool RemsetRegionPredicate(const Region *r)
1079 {
1080     // In case of mixed GC don't process remsets of the tenured regions which are in the collection set
1081     return !r->HasFlag(IS_COLLECTION_SET);
1082 }
1083 
1084 template <class LanguageConfig>
EvacuateCollectionSet(const GCTask & task)1085 void G1GC<LanguageConfig>::EvacuateCollectionSet(const GCTask &task)
1086 {
1087     ScopedTiming t(__FUNCTION__, *this->GetTiming());
1088     RemSet<> remset;
1089     MergeRemSet(&remset);
1090 
1091     for (auto *region : remset.GetDirtyRegions()) {
1092         // MarkBitmap is used instead of LiveBitmap in ScanRemset. See comment there.
1093         region->GetLiveBitmap()->CopyTo(region->GetMarkBitmap());
1094     }
1095 
1096     ParallelCompactionTask<LanguageConfig> parallelCompactionTask(this, this->GetG1ObjectAllocator(), &remset,
1097                                                                   collectionSet_);
1098     parallelCompactionTask.Run();
1099 
1100     this->CommonUpdateRefsToMovedObjects();
1101     HandleReferences(task);
1102     ActualizeRemSets();
1103 
1104     parallelCompactionTask.FlushDirtyCards(updatedRefsQueue_);
1105     parallelCompactionTask.Finish();
1106 
1107     analytics_.ReportEvacuatedBytes(parallelCompactionTask.GetCopiedBytesYoung());
1108 
1109     this->memStats_.template RecordSizeMovedYoung<false>(parallelCompactionTask.GetCopiedBytesYoung());
1110     this->memStats_.template RecordCountMovedYoung<false>(parallelCompactionTask.GetCopiedObjectsYoung());
1111     this->memStats_.template RecordSizeMovedTenured<false>(parallelCompactionTask.GetCopiedBytesOld());
1112     this->memStats_.template RecordCountMovedTenured<false>(parallelCompactionTask.GetCopiedObjectsOld());
1113     this->memStats_.template RecordSizeFreedYoung<false>(parallelCompactionTask.GetFreedBytesYoung());
1114     this->memStats_.template RecordSizeFreedTenured<false>(parallelCompactionTask.GetFreedBytesOld());
1115 
1116     this->GetPandaVm()->UpdateMovedStrings();
1117     SweepRegularVmRefs();
1118 
1119     ResetRegionAfterMixedGC();
1120 }
1121 
1122 template <class LanguageConfig>
MergeRemSet(RemSet<> * remset)1123 void G1GC<LanguageConfig>::MergeRemSet(RemSet<> *remset)
1124 {
1125     ScopedTiming t(__FUNCTION__, *this->GetTiming());
1126     for (auto *region : collectionSet_) {
1127         remset->Merge(region->GetRemSet());
1128     }
1129 
1130     constexpr size_t MEM_SIZE = DEFAULT_REGION_SIZE / RemSet<>::Bitmap::GetNumBits();
1131     auto *cardTable = this->GetCardTable();
1132     for (auto *card : dirtyCards_) {
1133         auto range = cardTable->GetMemoryRange(card);
1134         auto addr = range.GetStartAddress();
1135         auto region = ark::mem::AddrToRegion(ToVoidPtr(addr));
1136         if (!RemsetRegionPredicate(region)) {
1137             // Skip cards which correspond to regions in the collection set because live objects in collection set are
1138             // traversed during evacuation anyway.
1139             continue;
1140         }
1141         auto endAddr = range.GetEndAddress();
1142         while (addr < endAddr) {
1143             remset->AddRef(ToVoidPtr(addr));
1144             addr += MEM_SIZE;
1145         }
1146     }
1147 
1148     // All dirty cards which do not correspond to regions in the collection set are processed and reenqueued in case of
1149     // cross region references during evacuation, see EvacuationObjectPointerHandler::ProcessObjectPointer
1150     dirtyCards_.clear();
1151 }
1152 
1153 template <class LanguageConfig>
HandleReferences(const GCTask & task)1154 void G1GC<LanguageConfig>::HandleReferences(const GCTask &task)
1155 {
1156     ScopedTiming t(__FUNCTION__, *this->GetTiming());
1157     auto refClearPred = [this](const ObjectHeader *obj) { return this->InGCSweepRange(obj); };
1158     this->ProcessReferences(task, refClearPred);
1159     this->GetPandaVm()->GetGlobalObjectStorage()->ClearWeakRefs(refClearPred);
1160     ProcessDirtyCards();
1161 }
1162 
1163 template <class LanguageConfig>
ResetRegionAfterMixedGC()1164 void G1GC<LanguageConfig>::ResetRegionAfterMixedGC()
1165 {
1166     auto *objectAllocator = this->GetG1ObjectAllocator();
1167     if (!collectionSet_.Young().empty()) {
1168         objectAllocator->ResetYoungAllocator();
1169     }
1170     {
1171         GCScope<TRACE_TIMING> resetRegions("ResetRegions", this);
1172         objectAllocator->template ResetRegions<RegionFlag::IS_OLD, RegionSpace::ReleaseRegionsPolicy::NoRelease,
1173                                                OSPagesPolicy::IMMEDIATE_RETURN, false>(collectionSet_.Tenured());
1174     }
1175 }
1176 
1177 template <class LanguageConfig>
RunGC(GCTask & task,const CollectionSet & collectibleRegions)1178 void G1GC<LanguageConfig>::RunGC(GCTask &task, const CollectionSet &collectibleRegions)
1179 {
1180     ASSERT(!this->IsFullGC());
1181     GCScope<TRACE_TIMING> scopedTrace(__FUNCTION__, this);
1182     LOG_DEBUG_GC << "GC start";
1183     uint64_t youngPauseTime;
1184     {
1185         time::Timer timer(&youngPauseTime, true);
1186         if (SinglePassCompactionAvailable()) {
1187             EvacuateCollectionSet(task);
1188         } else {
1189             MixedMarkAndCacheRefs(task, collectibleRegions);
1190             ClearYoungCards(collectibleRegions);
1191             ClearTenuredCards(collectibleRegions);
1192             CollectAndMove<false>(collectibleRegions);
1193         }
1194         analytics_.ReportSurvivedBytesRatio(collectibleRegions);
1195         ClearRefsFromRemsetsCache();
1196         this->GetObjectGenAllocator()->InvalidateSpaceData();
1197     }
1198     if (youngPauseTime > 0) {
1199         this->GetStats()->AddTimeValue(youngPauseTime, TimeTypeStats::YOUNG_PAUSED_TIME);
1200     }
1201     LOG_DEBUG_GC << "G1GC RunGC end";
1202 }
1203 
1204 template <class LanguageConfig>
SinglePassCompactionAvailable()1205 bool G1GC<LanguageConfig>::SinglePassCompactionAvailable()
1206 {
1207     if (!this->GetSettings()->G1SinglePassCompactionEnabled()) {
1208         return false;
1209     }
1210 
1211     if (this->GetSettings()->G1EnablePauseTimeGoal()) {
1212         // pause time goal should be corrected for single pass collection
1213         return false;
1214     }
1215 
1216     if (!this->GetPandaVm()->SupportGCSinglePassCompaction()) {
1217         return false;
1218     }
1219 
1220     if (g1PromotionRegionAliveRate_ <= 0) {
1221         return false;
1222     }
1223 
1224     for (auto *region : collectionSet_) {
1225         if (region->HasPinnedObjects()) {
1226             return false;
1227         }
1228     }
1229 
1230     auto predictedSurvivedBytesRatio = analytics_.PredictSurvivedBytesRatio();
1231     if (predictedSurvivedBytesRatio == 0) {
1232         // threre are not statistics, starts with GC which is able to promote whole regions
1233         return false;
1234     }
1235 
1236     // uses single pass collection for low survival ratio
1237     return predictedSurvivedBytesRatio * PERCENT_100_D < g1PromotionRegionAliveRate_;
1238 }
1239 
1240 template <class LanguageConfig>
MixedMarkAndCacheRefs(const GCTask & task,const CollectionSet & collectibleRegions)1241 void G1GC<LanguageConfig>::MixedMarkAndCacheRefs(const GCTask &task, const CollectionSet &collectibleRegions)
1242 {
1243     GCScope<TRACE_TIMING_PHASE> scopedTrace(__FUNCTION__, this, GCPhase::GC_PHASE_MARK_YOUNG);
1244     bool useGcWorkers = this->GetSettings()->ParallelMarkingEnabled();
1245     GCMarkingStackType objectsStack(this, useGcWorkers ? this->GetSettings()->GCRootMarkingStackMaxSize() : 0,
1246                                     useGcWorkers ? this->GetSettings()->GCWorkersMarkingStackMaxSize() : 0,
1247                                     GCWorkersTaskTypes::TASK_MARKING,
1248                                     this->GetSettings()->GCMarkingStackNewTasksFrequency());
1249     for (Region *region : collectibleRegions) {
1250         region->GetMarkBitmap()->ClearAllBits();
1251         // Calculate live bytes during marking phase
1252         region->SetLiveBytes(0U);
1253     }
1254     ASSERT(this->GetReferenceProcessor()->GetReferenceQueueSize() ==
1255            0);  // all references should be processed on previous-gc
1256     // Iterate over roots and add other roots
1257     // 0. Pre-process refs queue and fill RemSets (should be done later in background)
1258     // Note: We need to process only tenured -> young refs,
1259     // since we reach this by graph from tenured roots,
1260     // because we will process all young regions at young GC we will find all required references
1261     RefCacheBuilder<LanguageConfig> builder(this, &uniqueRefsFromRemsets_, regionSizeBits_, &objectsStack);
1262     auto refsChecker = [this, &builder](Region *region, const MemRange &memRange) {
1263         IterateOverRefsInMemRange(memRange, region, builder);
1264         return false;
1265     };
1266 
1267     analytics_.ReportMarkingStart(ark::time::GetCurrentTimeInNanos());
1268     CacheRefsFromRemsets(refsChecker);
1269 
1270     auto refPred = [this](const ObjectHeader *obj) { return this->InGCSweepRange(obj); };
1271     GCRootVisitor gcMarkCollectionSet = [&objectsStack, this, &refPred](const GCRoot &gcRoot) {
1272         ObjectHeader *rootObject = gcRoot.GetObjectHeader();
1273         ObjectHeader *fromObject = gcRoot.GetFromObjectHeader();
1274         LOG_DEBUG_GC << "Handle root " << GetDebugInfoAboutObject(rootObject) << " from: " << gcRoot.GetType();
1275         if (UNLIKELY(fromObject != nullptr) &&
1276             this->IsReference(fromObject->NotAtomicClassAddr<BaseClass>(), fromObject, refPred)) {
1277             LOG_DEBUG_GC << "Add reference: " << GetDebugInfoAboutObject(fromObject) << " to stack";
1278             mixedMarker_.Mark(fromObject);
1279             this->ProcessReference(&objectsStack, fromObject->NotAtomicClassAddr<BaseClass>(), fromObject,
1280                                    GC::EmptyReferenceProcessPredicate);
1281         } else {
1282             // Skip non-collection-set roots
1283             auto rootObjectPtr = gcRoot.GetObjectHeader();
1284             ASSERT(rootObjectPtr != nullptr);
1285             if (mixedMarker_.MarkIfNotMarked(rootObjectPtr)) {
1286                 ASSERT(this->InGCSweepRange(rootObjectPtr));
1287                 LOG_DEBUG_GC << "root " << GetDebugInfoAboutObject(rootObjectPtr);
1288                 objectsStack.PushToStack(gcRoot.GetType(), rootObjectPtr);
1289             } else {
1290                 LOG_DEBUG_GC << "Skip root for young mark: " << std::hex << rootObjectPtr;
1291             }
1292         }
1293     };
1294 
1295     {
1296         GCScope<TRACE_TIMING> markingCollectionSetRootsTrace("Marking roots collection-set", this);
1297 
1298         this->VisitRoots(gcMarkCollectionSet, VisitGCRootFlags::ACCESS_ROOT_NONE);
1299     }
1300     {
1301         GCScope<TRACE_TIMING> markStackTiming("MarkStack", this);
1302         this->MarkStackMixed(&objectsStack);
1303         ASSERT(objectsStack.Empty());
1304         if (useGcWorkers) {
1305             GCScope<TRACE_TIMING> waitingTiming("WaitUntilTasksEnd", this);
1306             this->GetWorkersTaskPool()->WaitUntilTasksEnd();
1307         }
1308     }
1309 
1310     auto refClearPred = [this](const ObjectHeader *obj) { return this->InGCSweepRange(obj); };
1311     this->GetPandaVm()->HandleReferences(task, refClearPred);
1312 
1313     analytics_.ReportMarkingEnd(ark::time::GetCurrentTimeInNanos(), GetUniqueRemsetRefsCount());
1314 
1315     // HandleReferences could write a new barriers - so we need to handle them before moving
1316     ProcessDirtyCards();
1317 }
1318 
1319 template <class LanguageConfig>
CollectVerificationInfo(const CollectionSet & collectionSet)1320 HeapVerifierIntoGC<LanguageConfig> G1GC<LanguageConfig>::CollectVerificationInfo(const CollectionSet &collectionSet)
1321 {
1322     HeapVerifierIntoGC<LanguageConfig> collectVerifier(this->GetPandaVm()->GetHeapManager());
1323     if (this->GetSettings()->IntoGCHeapVerification()) {
1324         ScopedTiming collectVerificationTiming(__FUNCTION__, *this->GetTiming());
1325         PandaVector<MemRange> memRanges;
1326         memRanges.reserve(collectionSet.size());
1327         std::for_each(collectionSet.begin(), collectionSet.end(),
1328                       [&memRanges](const Region *region) { memRanges.emplace_back(region->Begin(), region->End()); });
1329         collectVerifier.CollectVerificationInfo(std::move(memRanges));
1330     }
1331     return collectVerifier;
1332 }
1333 
1334 template <class LanguageConfig>
VerifyCollectAndMove(HeapVerifierIntoGC<LanguageConfig> && collectVerifier,const CollectionSet & collectionSet)1335 void G1GC<LanguageConfig>::VerifyCollectAndMove(HeapVerifierIntoGC<LanguageConfig> &&collectVerifier,
1336                                                 const CollectionSet &collectionSet)
1337 {
1338     if (this->GetSettings()->IntoGCHeapVerification()) {
1339         ScopedTiming verificationTiming(__FUNCTION__, *this->GetTiming());
1340         PandaVector<MemRange> aliveMemRange;
1341         std::for_each(collectionSet.begin(), collectionSet.end(), [&aliveMemRange](const Region *region) {
1342             if (region->HasFlag(RegionFlag::IS_PROMOTED)) {
1343                 aliveMemRange.emplace_back(region->Begin(), region->End());
1344             }
1345         });
1346         size_t failsCount = collectVerifier.VerifyAll(std::move(aliveMemRange));
1347         if (this->GetSettings()->FailOnHeapVerification() && failsCount > 0U) {
1348             PandaStringStream logStream;
1349             logStream << "Collection set size: " << collectionSet.size() << "\n";
1350             for (const auto r : collectionSet) {
1351                 logStream << *r << (r->HasFlag(RegionFlag::IS_PROMOTED) ? " was promoted\n" : "\n");
1352             }
1353             LOG(FATAL, GC) << "Heap was corrupted during CollectAndMove GC phase, HeapVerifier found " << failsCount
1354                            << " corruptions\n"
1355                            << logStream.str();
1356         }
1357     }
1358 }
1359 
1360 template <class LanguageConfig>
1361 template <bool FULL_GC>
UpdateRefsAndClear(const CollectionSet & collectionSet,MovedObjectsContainer<FULL_GC> * movedObjectsContainer,PandaVector<PandaVector<ObjectHeader * > * > * movedObjectsVector,HeapVerifierIntoGC<LanguageConfig> * collectVerifier)1362 void G1GC<LanguageConfig>::UpdateRefsAndClear(const CollectionSet &collectionSet,
1363                                               MovedObjectsContainer<FULL_GC> *movedObjectsContainer,
1364                                               PandaVector<PandaVector<ObjectHeader *> *> *movedObjectsVector,
1365                                               HeapVerifierIntoGC<LanguageConfig> *collectVerifier)
1366 {
1367     {
1368         os::memory::LockHolder lock(queueLock_);
1369         analytics_.ReportUpdateRefsStart(ark::time::GetCurrentTimeInNanos());
1370         if (this->GetSettings()->ParallelRefUpdatingEnabled()) {
1371             UpdateRefsToMovedObjects<FULL_GC, true>(movedObjectsContainer);
1372         } else {
1373             UpdateRefsToMovedObjects<FULL_GC, false>(movedObjectsContainer);
1374         }
1375         analytics_.ReportUpdateRefsEnd(ark::time::GetCurrentTimeInNanos());
1376         ActualizeRemSets();
1377     }
1378 
1379     VerifyCollectAndMove(std::move(*collectVerifier), collectionSet);
1380     SweepRegularVmRefs();
1381 
1382     auto objectAllocator = this->GetG1ObjectAllocator();
1383     if (!collectionSet.Young().empty()) {
1384         objectAllocator->ResetYoungAllocator();
1385     }
1386     {
1387         GCScope<TRACE_TIMING> resetRegions("ResetRegions", this);
1388         if (!this->IsFullGC()) {
1389             objectAllocator->template ResetRegions<RegionFlag::IS_OLD, RegionSpace::ReleaseRegionsPolicy::NoRelease,
1390                                                    OSPagesPolicy::IMMEDIATE_RETURN, false>(collectionSet.Tenured());
1391         } else {
1392             objectAllocator->template ResetRegions<RegionFlag::IS_OLD, RegionSpace::ReleaseRegionsPolicy::Release,
1393                                                    OSPagesPolicy::NO_RETURN, false>(collectionSet.Tenured());
1394         }
1395     }
1396     {
1397         // Don't forget to delete all temporary elements
1398         GCScope<TRACE_TIMING> clearMovedObjects("ClearMovedObjects", this);
1399         auto internalAllocator = this->GetInternalAllocator();
1400         if constexpr (FULL_GC) {
1401             bool useGcWorkers = this->GetSettings()->ParallelCompactingEnabled();
1402             if (useGcWorkers) {
1403                 for (auto r : *movedObjectsVector) {
1404                     internalAllocator->Delete(r);
1405                 }
1406             } else {
1407                 ASSERT(movedObjectsVector->size() == 1);
1408                 internalAllocator->Delete(movedObjectsVector->back());
1409             }
1410         } else {
1411             for (auto r : mixedMarkedObjects_) {
1412                 internalAllocator->Delete(r);
1413             }
1414             mixedMarkedObjects_.clear();
1415         }
1416     }
1417 }
1418 
1419 template <class LanguageConfig>
1420 template <bool FULL_GC>
1421 // NOLINTNEXTLINE(readability-function-size)
CollectAndMove(const CollectionSet & collectionSet)1422 bool G1GC<LanguageConfig>::CollectAndMove(const CollectionSet &collectionSet)
1423 {
1424     GCScope<TRACE_TIMING_PHASE> scope(__FUNCTION__, this, GCPhase::GC_PHASE_COLLECT_YOUNG_AND_MOVE);
1425     LOG_DEBUG_GC << "== G1GC CollectAndMove start ==";
1426     auto internalAllocator = this->GetInternalAllocator();
1427     bool useGcWorkers = this->GetSettings()->ParallelCompactingEnabled();
1428 
1429     PandaVector<PandaVector<ObjectHeader *> *> movedObjectsVector;
1430     HeapVerifierIntoGC<LanguageConfig> collectVerifier = this->CollectVerificationInfo(collectionSet);
1431     {
1432         GCScope<TRACE_TIMING> compactRegions("CompactRegions", this);
1433         analytics_.ReportEvacuationStart(ark::time::GetCurrentTimeInNanos());
1434         if constexpr (FULL_GC) {
1435             if (!useGcWorkers) {
1436                 auto vector = internalAllocator->template New<PandaVector<ObjectHeader *>>();
1437                 movedObjectsVector.push_back(vector);
1438             }
1439         }
1440         for (auto r : collectionSet.Young()) {
1441             this->DoRegionCompacting<RegionFlag::IS_EDEN, FULL_GC>(r, useGcWorkers, &movedObjectsVector);
1442         }
1443         for (auto r : collectionSet.Tenured()) {
1444             this->DoRegionCompacting<RegionFlag::IS_OLD, FULL_GC>(r, useGcWorkers, &movedObjectsVector);
1445         }
1446 
1447         if (useGcWorkers) {
1448             this->GetWorkersTaskPool()->WaitUntilTasksEnd();
1449         }
1450 
1451         analytics_.ReportEvacuationEnd(ark::time::GetCurrentTimeInNanos());
1452     }
1453 
1454     MovedObjectsContainer<FULL_GC> *movedObjectsContainer = nullptr;
1455     if constexpr (FULL_GC) {
1456         movedObjectsContainer = &movedObjectsVector;
1457     } else {
1458         movedObjectsContainer = &mixedMarkedObjects_;
1459     }
1460 
1461     UpdateRefsAndClear<FULL_GC>(collectionSet, movedObjectsContainer, &movedObjectsVector, &collectVerifier);
1462 
1463     LOG_DEBUG_GC << "== G1GC CollectAndMove end ==";
1464     return true;
1465 }
1466 
1467 template <class LanguageConfig>
1468 template <bool FULL_GC, bool NEED_LOCK>
1469 std::conditional_t<FULL_GC, UpdateRemsetRefUpdater<LanguageConfig, NEED_LOCK>, EnqueueRemsetRefUpdater<LanguageConfig>>
CreateRefUpdater(GCG1BarrierSet::ThreadLocalCardQueues * updatedRefQueue) const1470 G1GC<LanguageConfig>::CreateRefUpdater([[maybe_unused]] GCG1BarrierSet::ThreadLocalCardQueues *updatedRefQueue) const
1471 {
1472     if constexpr (FULL_GC) {
1473         return UpdateRemsetRefUpdater<LanguageConfig, NEED_LOCK>(regionSizeBits_);
1474     } else {
1475         return EnqueueRemsetRefUpdater<LanguageConfig>(this->GetCardTable(), updatedRefQueue, regionSizeBits_);
1476     }
1477 }
1478 
1479 template <class LanguageConfig>
1480 template <class ObjectsContainer>
ProcessMovedObjects(ObjectsContainer * movedObjects)1481 void G1GC<LanguageConfig>::ProcessMovedObjects(ObjectsContainer *movedObjects)
1482 {
1483     auto rangeBegin = movedObjects->begin();
1484     auto rangeEnd = rangeBegin;
1485     while (rangeBegin != movedObjects->end()) {
1486         if (std::distance(rangeBegin, movedObjects->end()) < GCUpdateRefsWorkersTask<false>::RANGE_SIZE) {
1487             rangeEnd = movedObjects->end();
1488         } else {
1489             std::advance(rangeEnd, GCUpdateRefsWorkersTask<false>::RANGE_SIZE);
1490         }
1491         auto *movedObjectsRange =
1492             this->GetInternalAllocator()->template New<typename GCUpdateRefsWorkersTask<false>::MovedObjectsRange>(
1493                 rangeBegin, rangeEnd);
1494         rangeBegin = rangeEnd;
1495         GCUpdateRefsWorkersTask<false> gcWorkerTask(movedObjectsRange);
1496         if (this->GetWorkersTaskPool()->AddTask(GCUpdateRefsWorkersTask<false>(gcWorkerTask))) {
1497             continue;
1498         }
1499         // Couldn't add new task, so do task processing immediately
1500         this->WorkerTaskProcessing(&gcWorkerTask, nullptr);
1501     }
1502 }
1503 
1504 template <class LanguageConfig>
1505 template <bool FULL_GC, bool ENABLE_WORKERS, class Visitor>
UpdateMovedObjectsReferences(MovedObjectsContainer<FULL_GC> * movedObjectsContainer,const Visitor & refUpdater)1506 void G1GC<LanguageConfig>::UpdateMovedObjectsReferences(MovedObjectsContainer<FULL_GC> *movedObjectsContainer,
1507                                                         const Visitor &refUpdater)
1508 {
1509     ScopedTiming t("UpdateMovedObjectsReferences", *this->GetTiming());
1510     for (auto *movedObjects : *movedObjectsContainer) {
1511         if constexpr (ENABLE_WORKERS) {
1512             ProcessMovedObjects(movedObjects);
1513         } else {  // GC workers are not used
1514             typename GCUpdateRefsWorkersTask<FULL_GC>::MovedObjectsRange movedObjectsRange(movedObjects->begin(),
1515                                                                                            movedObjects->end());
1516             DoUpdateReferencesToMovedObjectsRange<LanguageConfig, decltype(refUpdater), FULL_GC>(&movedObjectsRange,
1517                                                                                                  refUpdater);
1518         }
1519     }
1520 }
1521 
1522 template <class LanguageConfig>
1523 template <bool FULL_GC, bool USE_WORKERS>
UpdateRefsToMovedObjects(MovedObjectsContainer<FULL_GC> * movedObjectsContainer)1524 void G1GC<LanguageConfig>::UpdateRefsToMovedObjects(MovedObjectsContainer<FULL_GC> *movedObjectsContainer)
1525 {
1526     GCScope<TRACE_TIMING> scope(__FUNCTION__, this);
1527     // Currently lock for RemSet too much influences for pause, so don't use workers on FULL-GC
1528     constexpr bool ENABLE_WORKERS = USE_WORKERS && !FULL_GC;
1529     auto internalAllocator = this->GetInternalAllocator();
1530     auto *updatedRefQueue =
1531         (ENABLE_WORKERS) ? internalAllocator->template New<GCG1BarrierSet::ThreadLocalCardQueues>() : updatedRefsQueue_;
1532     // NEED_LOCK is true <=> when ENABLE_WORKERS is true
1533     auto refUpdater = this->CreateRefUpdater<FULL_GC, ENABLE_WORKERS>(updatedRefQueue);
1534     //  update reference from objects which were moved while garbage collection
1535     LOG_DEBUG_GC << "=== Update ex-cset -> ex-cset references. START. ===";
1536     UpdateMovedObjectsReferences<FULL_GC, ENABLE_WORKERS>(movedObjectsContainer, refUpdater);
1537     LOG_DEBUG_GC << "=== Update ex-cset -> ex-cset references. END. ===";
1538 
1539     // update references from objects which are not part of collection set
1540     LOG_DEBUG_GC << "=== Update non ex-cset -> ex-cset references. START. ===";
1541     if constexpr (FULL_GC) {
1542         UpdateRefsFromRemSets(refUpdater);
1543     } else {
1544         // We don't need to create Remset for promoted regions because we already have them
1545         if (!IsCollectionSetFullyPromoted()) {
1546             VisitRemSets(refUpdater);
1547         }
1548     }
1549     LOG_DEBUG_GC << "=== Update non ex-cset -> ex-cset references. END. ===";
1550     if constexpr (ENABLE_WORKERS) {
1551         {
1552             os::memory::LockHolder lock(gcWorkerQueueLock_);
1553             updatedRefsQueue_->insert(updatedRefsQueue_->end(), updatedRefQueue->begin(), updatedRefQueue->end());
1554             internalAllocator->Delete(updatedRefQueue);
1555         }
1556         GCScope<TRACE_TIMING> waitingTiming("WaitUntilTasksEnd", this);
1557         this->GetWorkersTaskPool()->WaitUntilTasksEnd();
1558     }
1559     this->CommonUpdateRefsToMovedObjects();
1560 }
1561 
1562 template <class LanguageConfig>
OnPauseMark(GCTask & task,GCMarkingStackType * objectsStack,bool useGcWorkers)1563 NO_THREAD_SAFETY_ANALYSIS void G1GC<LanguageConfig>::OnPauseMark(GCTask &task, GCMarkingStackType *objectsStack,
1564                                                                  bool useGcWorkers)
1565 {
1566     GCScope<TRACE_TIMING> scope(__FUNCTION__, this);
1567     LOG_DEBUG_GC << "OnPause marking started";
1568     auto *objectAllocator = GetG1ObjectAllocator();
1569     this->MarkImpl(
1570         &marker_, objectsStack, CardTableVisitFlag::VISIT_DISABLED,
1571         // process references on FULL-GC
1572         GC::EmptyReferenceProcessPredicate,
1573         // non-young mem-range checker
1574         [objectAllocator](MemRange &memRange) { return !objectAllocator->IsIntersectedWithYoung(memRange); },
1575         // mark predicate
1576         CalcLiveBytesMarkPreprocess);
1577     if (useGcWorkers) {
1578         this->GetWorkersTaskPool()->WaitUntilTasksEnd();
1579     }
1580     /**
1581      * We don't collect non-movable regions right now, if there was a reference from non-movable to
1582      * young/tenured region then we reset markbitmap for non-nonmovable, but don't update livebitmap and we
1583      * can traverse over non-reachable object (in CacheRefsFromRemsets) and visit DEAD object in
1584      * tenured space (was delete on young-collection or in Iterative-full-gc phase.
1585      */
1586     auto refClearPred = []([[maybe_unused]] const ObjectHeader *obj) { return true; };
1587     this->GetPandaVm()->HandleReferences(task, refClearPred);
1588 }
1589 
1590 template <class LanguageConfig>
FullMarking(ark::GCTask & task)1591 void G1GC<LanguageConfig>::FullMarking(ark::GCTask &task)
1592 {
1593     GCScope<TRACE_TIMING_PHASE> scope(__FUNCTION__, this, GCPhase::GC_PHASE_MARK);
1594     auto *objectAllocator = GetG1ObjectAllocator();
1595     bool useGcWorkers = this->GetSettings()->ParallelMarkingEnabled();
1596 
1597     GCMarkingStackType fullCollectionStack(this, useGcWorkers ? this->GetSettings()->GCRootMarkingStackMaxSize() : 0,
1598                                            useGcWorkers ? this->GetSettings()->GCWorkersMarkingStackMaxSize() : 0,
1599                                            GCWorkersTaskTypes::TASK_FULL_MARK,
1600                                            this->GetSettings()->GCMarkingStackNewTasksFrequency());
1601 
1602     InitialMark(fullCollectionStack);
1603 
1604     this->OnPauseMark(task, &fullCollectionStack, useGcWorkers);
1605     // We will sweep VM refs in tenured space during mixed collection, but only for non empty regions.
1606     // therefore, sweep it here only for NonMovable, Humongous objects, and empty movable regions:
1607     SweepNonRegularVmRefs();
1608     auto allRegions = objectAllocator->GetAllRegions();
1609     for (auto *r : allRegions) {
1610         if (r->GetLiveBitmap() != nullptr) {
1611             r->CloneMarkBitmapToLiveBitmap();
1612         }
1613     }
1614     // Force card updater here, after swapping bitmap, to skip dead objects
1615     ProcessDirtyCards();
1616     auto garbageRegions = GetG1ObjectAllocator()->template GetTopGarbageRegions<false>();
1617     auto emptyTenuredRegions = GetEmptyTenuredRegularRegionsFromQueue(std::move(garbageRegions));
1618     CollectEmptyRegions<false, false>(task, &emptyTenuredRegions);
1619 }
1620 
1621 template <class LanguageConfig>
ConcurrentMarking(ark::GCTask & task)1622 void G1GC<LanguageConfig>::ConcurrentMarking(ark::GCTask &task)
1623 {
1624     {
1625         PauseTimeGoalDelay();
1626         auto scopedTracker = g1PauseTracker_.CreateScope();
1627         GCScopedPauseStats scopedPauseStats(this->GetPandaVm()->GetGCStats(), nullptr, PauseTypeStats::COMMON_PAUSE);
1628         InitialMark(concurrentMarkingStack_);
1629     }
1630 
1631     LOG_DEBUG_GC << "Concurrent marking started";
1632     ConcurrentMark(&concurrentMarkingStack_);
1633     PauseTimeGoalDelay();
1634     // weak refs shouldn't be added to the queue on concurrent-mark
1635     ASSERT(this->GetReferenceProcessor()->GetReferenceQueueSize() == 0);
1636 
1637     DisablePreWrbInThreads();
1638 
1639     concurrentMarkingFlag_ = false;
1640     if (!interruptConcurrentFlag_) {
1641         Remark(task);
1642         // Enable mixed GC
1643         auto garbageRegions = GetG1ObjectAllocator()->template GetTopGarbageRegions<false>();
1644         if (HaveGarbageRegions(garbageRegions)) {
1645             // Atomic with release order reason: to see changes made by GC thread (which do concurrent marking
1646             // and than set isMixedGcRequired_) in mutator thread which waits for the end of concurrent
1647             // marking.
1648             isMixedGcRequired_.store(true, std::memory_order_release);
1649         }
1650 
1651         {
1652             ScopedTiming t("Concurrent Sweep", *this->GetTiming());
1653             ConcurrentScope concurrentScope(this);
1654             auto emptyTenuredRegions = GetEmptyTenuredRegularRegionsFromQueue(std::move(garbageRegions));
1655             if (this->IsConcurrencyAllowed()) {
1656                 CollectEmptyRegions<true, true>(task, &emptyTenuredRegions);
1657             } else {
1658                 CollectEmptyRegions<false, false>(task, &emptyTenuredRegions);
1659             }
1660         }
1661     } else {
1662         concurrentMarkingStack_.Clear();
1663         ClearSatb();
1664     }
1665     ASSERT(concurrentMarkingStack_.Empty());
1666 }
1667 
1668 template <class LanguageConfig>
PauseTimeGoalDelay()1669 void G1GC<LanguageConfig>::PauseTimeGoalDelay()
1670 {
1671     if (this->GetSettings()->G1EnablePauseTimeGoal() && !interruptConcurrentFlag_) {
1672         auto start = ark::time::GetCurrentTimeInMicros();
1673         // Instead of max pause it should be estimated to calculate delay
1674         auto remained = g1PauseTracker_.MinDelayBeforeMaxPauseInMicros(ark::time::GetCurrentTimeInMicros());
1675         if (remained > 0) {
1676             ConcurrentScope concurrentScope(this);
1677             os::memory::LockHolder lh(concurrentMarkMutex_);
1678             while (!interruptConcurrentFlag_ && remained > 0) {
1679                 auto ms = static_cast<uint64_t>(remained) / ark::os::time::MILLIS_TO_MICRO;
1680                 auto ns = (static_cast<uint64_t>(remained) - ms * ark::os::time::MILLIS_TO_MICRO) *
1681                           ark::os::time::MICRO_TO_NANO;
1682                 concurrentMarkCondVar_.TimedWait(&concurrentMarkMutex_, ms, ns);
1683                 auto d = static_cast<int64_t>(ark::time::GetCurrentTimeInMicros() - start);
1684                 remained -= d;
1685             }
1686         }
1687     }
1688 }
1689 
1690 template <class LanguageConfig>
InitialMark(GCMarkingStackType & markingStack)1691 void G1GC<LanguageConfig>::InitialMark(GCMarkingStackType &markingStack)
1692 {
1693     {
1694         // First we need to unmark all heap
1695         GCScope<TRACE_TIMING> unMarkScope("UnMark", this);
1696         LOG_DEBUG_GC << "Start unmark all heap before mark";
1697         auto allRegion = GetG1ObjectAllocator()->GetAllRegions();
1698         for (Region *r : allRegion) {
1699             auto *bitmap = r->GetMarkBitmap();
1700             // Calculate live bytes during mark-phase
1701             r->SetLiveBytes(0U);
1702             // unmark full-heap except Humongous-space
1703             bitmap->ClearAllBits();
1704         }
1705 #ifndef NDEBUG
1706         this->GetObjectAllocator()->IterateOverObjects(
1707             [this](ObjectHeader *obj) { ASSERT(!this->marker_.IsMarked(obj)); });
1708 #endif
1709     }
1710     ASSERT(this->GetReferenceProcessor()->GetReferenceQueueSize() ==
1711            0);  // all references should be processed on mixed-gc
1712     {
1713         GCScope<TRACE_TIMING_PHASE> initialMarkScope("InitialMark", this, GCPhase::GC_PHASE_INITIAL_MARK);
1714         // Collect non-heap roots.
1715         // Mark the whole heap by using only these roots.
1716         // The interregion roots will be processed at pause
1717 
1718         // InitialMark. STW
1719         GCRootVisitor gcMarkRoots = [this, &markingStack](const GCRoot &gcRoot) {
1720             ValidateObject(gcRoot.GetType(), gcRoot.GetObjectHeader());
1721             if (marker_.MarkIfNotMarked(gcRoot.GetObjectHeader())) {
1722                 markingStack.PushToStack(gcRoot.GetType(), gcRoot.GetObjectHeader());
1723             }
1724         };
1725         this->VisitRoots(gcMarkRoots, VisitGCRootFlags::ACCESS_ROOT_ALL);
1726     }
1727 }
1728 
1729 template <class LanguageConfig>
ConcurrentMark(GCMarkingStackType * objectsStack)1730 void G1GC<LanguageConfig>::ConcurrentMark(GCMarkingStackType *objectsStack)
1731 {
1732     ConcurrentScope concurrentScope(this);
1733     GCScope<TRACE_TIMING_PHASE> scope(__FUNCTION__, this, GCPhase::GC_PHASE_MARK);
1734     this->ConcurentMarkImpl(objectsStack);
1735 }
1736 
1737 template <class LanguageConfig>
Remark(ark::GCTask const & task)1738 void G1GC<LanguageConfig>::Remark(ark::GCTask const &task)
1739 {
1740     /**
1741      * Make remark on pause to have all marked objects in tenured space, it gives possibility to check objects in
1742      * remsets. If they are not marked - we don't process this object, because it's dead already
1743      */
1744     auto scopedTracker = g1PauseTracker_.CreateScope();
1745     GCScope<TIMING_PHASE> gcScope(__FUNCTION__, this, GCPhase::GC_PHASE_REMARK);
1746     GCScopedPauseStats scopedPauseStats(this->GetPandaVm()->GetGCStats(), nullptr, PauseTypeStats::REMARK_PAUSE);
1747     {
1748         ScopedTiming t("Stack Remarking", *this->GetTiming());
1749         bool useGcWorkers = this->GetSettings()->ParallelMarkingEnabled();
1750         GCMarkingStackType stack(this, useGcWorkers ? this->GetSettings()->GCRootMarkingStackMaxSize() : 0,
1751                                  useGcWorkers ? this->GetSettings()->GCWorkersMarkingStackMaxSize() : 0,
1752                                  GCWorkersTaskTypes::TASK_REMARK,
1753                                  this->GetSettings()->GCMarkingStackNewTasksFrequency());
1754 
1755         // The mutator may create new regions.
1756         // If so we should bind bitmaps of new regions.
1757         DrainSatb(&stack);
1758         this->MarkStack(&marker_, &stack, CalcLiveBytesMarkPreprocess);
1759 
1760         if (useGcWorkers) {
1761             this->GetWorkersTaskPool()->WaitUntilTasksEnd();
1762         }
1763 
1764         // ConcurrentMark doesn't visit young objects - so we can't clear references which are in young-space because we
1765         // don't know which objects are marked. We will process them on young/mixed GC separately later, here we process
1766         // only refs in tenured-space
1767         auto refClearPred = []([[maybe_unused]] const ObjectHeader *obj) {
1768             return !ObjectToRegion(obj)->HasFlag(RegionFlag::IS_EDEN);
1769         };
1770         this->GetPandaVm()->HandleReferences(task, refClearPred);
1771     }
1772 
1773     // We will sweep VM refs in tenured space during mixed collection,
1774     // therefore, sweep it here only for NonMovable and Humongous objects:
1775     SweepNonRegularVmRefs();
1776     auto g1Allocator = this->GetG1ObjectAllocator();
1777     auto allRegions = g1Allocator->GetAllRegions();
1778     for (const auto &region : allRegions) {
1779         if (region->HasFlag(IS_OLD) || region->HasFlag(IS_NONMOVABLE)) {
1780             region->SwapMarkBitmap();
1781         }
1782     }
1783     // Force card updater here, after swapping bitmap, to skip dead objects
1784     ProcessDirtyCards();
1785 }
1786 
1787 template <class LanguageConfig>
SweepNonRegularVmRefs()1788 void G1GC<LanguageConfig>::SweepNonRegularVmRefs()
1789 {
1790     ScopedTiming scopedTiming(__FUNCTION__, *this->GetTiming());
1791 
1792     this->GetPandaVm()->SweepVmRefs([this](ObjectHeader *object) {
1793         Region *region = ObjectToRegion(object);
1794         if (region->HasFlag(RegionFlag::IS_EDEN)) {
1795             return ObjectStatus::ALIVE_OBJECT;
1796         }
1797         bool nonRegularObject =
1798             region->HasFlag(RegionFlag::IS_NONMOVABLE) || region->HasFlag(RegionFlag::IS_LARGE_OBJECT);
1799         if (!nonRegularObject) {
1800             ASSERT(region->GetLiveBytes() != 0U || !this->IsMarked(object));
1801             return region->GetLiveBytes() == 0U ? ObjectStatus::DEAD_OBJECT : ObjectStatus::ALIVE_OBJECT;
1802         }
1803         return this->IsMarked(object) ? ObjectStatus::ALIVE_OBJECT : ObjectStatus::DEAD_OBJECT;
1804     });
1805 }
1806 
1807 template <class LanguageConfig>
SweepRegularVmRefs()1808 void G1GC<LanguageConfig>::SweepRegularVmRefs()
1809 {
1810     ScopedTiming t(__FUNCTION__, *this->GetTiming());
1811 
1812     this->GetPandaVm()->SweepVmRefs([this](ObjectHeader *obj) {
1813         if (this->InGCSweepRange(obj)) {
1814             return ObjectStatus::DEAD_OBJECT;
1815         }
1816         return ObjectStatus::ALIVE_OBJECT;
1817     });
1818 }
1819 
1820 template <class LanguageConfig>
GetCollectibleRegions(ark::GCTask const & task,bool isMixed)1821 CollectionSet G1GC<LanguageConfig>::GetCollectibleRegions(ark::GCTask const &task, bool isMixed)
1822 {
1823     ASSERT(!this->IsFullGC());
1824     ScopedTiming scopedTiming(__FUNCTION__, *this->GetTiming());
1825     auto g1Allocator = this->GetG1ObjectAllocator();
1826     LOG_DEBUG_GC << "Start GetCollectibleRegions isMixed: " << isMixed << " reason: " << task.reason;
1827     CollectionSet collectionSet(g1Allocator->GetYoungRegions());
1828     if (isMixed) {
1829         if (!this->GetSettings()->G1EnablePauseTimeGoal()) {
1830             AddOldRegionsMaxAllowed(collectionSet);
1831         } else {
1832             AddOldRegionsAccordingPauseTimeGoal(collectionSet);
1833         }
1834     }
1835     LOG_DEBUG_GC << "collectibleRegions size: " << collectionSet.size() << " young " << collectionSet.Young().size()
1836                  << " old " << std::distance(collectionSet.Young().end(), collectionSet.end())
1837                  << " reason: " << task.reason << " isMixed: " << isMixed;
1838     return collectionSet;
1839 }
1840 
1841 template <class LanguageConfig>
AddOldRegionsMaxAllowed(CollectionSet & collectionSet)1842 void G1GC<LanguageConfig>::AddOldRegionsMaxAllowed(CollectionSet &collectionSet)
1843 {
1844     auto regions = this->GetG1ObjectAllocator()->template GetTopGarbageRegions<false>();
1845     for (size_t i = 0; i < numberOfMixedTenuredRegions_ && !regions.empty(); i++) {
1846         auto *garbageRegion = regions.top().second;
1847         regions.pop();
1848         ASSERT(!garbageRegion->HasFlag(IS_EDEN));
1849         ASSERT(!garbageRegion->HasPinnedObjects());
1850         ASSERT(!garbageRegion->HasFlag(IS_RESERVED));
1851         ASSERT(garbageRegion->GetAllocatedBytes() != 0U);
1852         double garbageRate = static_cast<double>(garbageRegion->GetGarbageBytes()) / garbageRegion->GetAllocatedBytes();
1853         if (garbageRate >= regionGarbageRateThreshold_) {
1854             LOG_DEBUG_GC << "Garbage percentage in " << std::hex << garbageRegion << " region = " << std::dec
1855                          << garbageRate << " %, add to collection set";
1856             collectionSet.AddRegion(garbageRegion);
1857         } else {
1858             LOG_DEBUG_GC << "Garbage percentage in " << std::hex << garbageRegion << " region = " << std::dec
1859                          << garbageRate << " %, don't add to collection set";
1860             break;
1861         }
1862     }
1863 }
1864 
1865 template <class LanguageConfig>
AddOldRegionsAccordingPauseTimeGoal(CollectionSet & collectionSet)1866 void G1GC<LanguageConfig>::AddOldRegionsAccordingPauseTimeGoal(CollectionSet &collectionSet)
1867 {
1868     auto gcPauseTimeBudget = this->GetSettings()->GetG1MaxGcPauseInMillis() * ark::os::time::MILLIS_TO_MICRO;
1869     auto candidates = this->GetG1ObjectAllocator()->template GetTopGarbageRegions<false>();
1870     // add at least one old region to guarantee a progress in mixed collection
1871     auto *topRegion = candidates.top().second;
1872     collectionSet.AddRegion(topRegion);
1873     auto expectedYoungCollectionTime = analytics_.PredictYoungCollectionTimeInMicros(collectionSet);
1874     auto expectedTopRegionCollectionTime = analytics_.PredictOldCollectionTimeInMicros(topRegion);
1875     auto totalPredictedPause = expectedYoungCollectionTime + expectedTopRegionCollectionTime;
1876     if (gcPauseTimeBudget < expectedTopRegionCollectionTime) {
1877         LOG_DEBUG_GC << "Not enough budget to add more than one old region";
1878         analytics_.ReportPredictedMixedPause(totalPredictedPause);
1879         return;
1880     }
1881     gcPauseTimeBudget -= expectedTopRegionCollectionTime;
1882     if (gcPauseTimeBudget < expectedYoungCollectionTime) {
1883         LOG_DEBUG_GC << "Not enough budget to add old regions";
1884         analytics_.ReportPredictedMixedPause(totalPredictedPause);
1885         return;
1886     }
1887     gcPauseTimeBudget -= expectedYoungCollectionTime;
1888     auto expectedScanDirtyCardsTime = analytics_.PredictScanDirtyCardsTime(dirtyCards_.size());
1889     if (gcPauseTimeBudget < expectedScanDirtyCardsTime) {
1890         LOG_DEBUG_GC << "Not enough budget to add old regions after scanning dirty cards";
1891         analytics_.ReportPredictedMixedPause(totalPredictedPause);
1892         return;
1893     }
1894     gcPauseTimeBudget -= expectedScanDirtyCardsTime;
1895     totalPredictedPause += expectedScanDirtyCardsTime;
1896 
1897     candidates.pop();
1898     totalPredictedPause += AddMoreOldRegionsAccordingPauseTimeGoal(collectionSet, candidates, gcPauseTimeBudget);
1899     analytics_.ReportPredictedMixedPause(totalPredictedPause);
1900 }
1901 
1902 template <class LanguageConfig>
AddMoreOldRegionsAccordingPauseTimeGoal(CollectionSet & collectionSet,PandaPriorityQueue<std::pair<uint32_t,Region * >> candidates,uint64_t gcPauseTimeBudget)1903 uint64_t G1GC<LanguageConfig>::AddMoreOldRegionsAccordingPauseTimeGoal(
1904     CollectionSet &collectionSet, PandaPriorityQueue<std::pair<uint32_t, Region *>> candidates,
1905     uint64_t gcPauseTimeBudget)
1906 {
1907     uint64_t time = 0;
1908     while (!candidates.empty()) {
1909         auto &scoreAndRegion = candidates.top();
1910         auto *garbageRegion = scoreAndRegion.second;
1911         ASSERT(!garbageRegion->HasFlag(IS_EDEN));
1912         ASSERT(!garbageRegion->HasPinnedObjects());
1913         ASSERT(!garbageRegion->HasFlag(IS_RESERVED));
1914         ASSERT(garbageRegion->GetAllocatedBytes() != 0U);
1915 
1916         candidates.pop();
1917 
1918         auto garbageRate = static_cast<double>(garbageRegion->GetGarbageBytes()) / garbageRegion->GetAllocatedBytes();
1919         if (garbageRate < regionGarbageRateThreshold_) {
1920             LOG_DEBUG_GC << "Garbage percentage in " << std::hex << garbageRegion << " region = " << std::dec
1921                          << garbageRate << " %, don't add to collection set";
1922             break;
1923         }
1924 
1925         auto expectedRegionCollectionTime = analytics_.PredictOldCollectionTimeInMicros(garbageRegion);
1926         if (gcPauseTimeBudget < expectedRegionCollectionTime) {
1927             LOG_DEBUG_GC << "Not enough budget to add old regions anymore";
1928             break;
1929         }
1930 
1931         gcPauseTimeBudget -= expectedRegionCollectionTime;
1932         time += expectedRegionCollectionTime;
1933 
1934         LOG_DEBUG_GC << "Garbage percentage in " << std::hex << garbageRegion << " region = " << std::dec << garbageRate
1935                      << " %, add to collection set";
1936         collectionSet.AddRegion(garbageRegion);
1937     }
1938     return time;
1939 }
1940 
1941 template <class LanguageConfig>
GetFullCollectionSet()1942 CollectionSet G1GC<LanguageConfig>::GetFullCollectionSet()
1943 {
1944     ASSERT(this->IsFullGC());
1945     // FillRemSet should be always finished before GetCollectibleRegions
1946     ASSERT(updateRemsetWorker_->GetQueueSize() == 0);
1947     auto g1Allocator = this->GetG1ObjectAllocator();
1948     g1Allocator->ClearCurrentTenuredRegion();
1949     CollectionSet collectionSet(g1Allocator->GetYoungRegions());
1950     auto movableGarbageRegions = g1Allocator->template GetTopGarbageRegions<true>();
1951     LOG_DEBUG_GC << "Regions for FullGC:";
1952     while (!movableGarbageRegions.empty()) {
1953         auto *region = movableGarbageRegions.top().second;
1954         movableGarbageRegions.pop();
1955         if (region->HasFlag(IS_EDEN) || region->HasPinnedObjects()) {
1956             LOG_DEBUG_GC << (region->HasFlags(IS_EDEN) ? "Young regions" : "Region with pinned objects") << " ("
1957                          << *region << ") is not added to collection set";
1958             continue;
1959         }
1960         LOG_DEBUG_GC << *region;
1961         ASSERT(!region->HasFlag(IS_NONMOVABLE) && !region->HasFlag(IS_LARGE_OBJECT));
1962         ASSERT(region->HasFlag(IS_OLD));
1963         collectionSet.AddRegion(region);
1964     }
1965     return collectionSet;
1966 }
1967 
1968 template <class LanguageConfig>
InterruptReleasePagesIfNeeded()1969 void G1GC<LanguageConfig>::InterruptReleasePagesIfNeeded()
1970 {
1971     if (this->GetSettings()->GCWorkersCount() != 0) {
1972         auto oldStatus = ReleasePagesStatus::RELEASING_PAGES;
1973         if (releasePagesInterruptFlag_.compare_exchange_strong(oldStatus, ReleasePagesStatus::NEED_INTERRUPT)) {
1974             /* @sync 1
1975              * @description Interrupt release pages
1976              */
1977         }
1978     }
1979 }
1980 
1981 template <class LanguageConfig>
StartReleasePagesIfNeeded(ReleasePagesStatus oldStatus)1982 void G1GC<LanguageConfig>::StartReleasePagesIfNeeded(ReleasePagesStatus oldStatus)
1983 {
1984     if (releasePagesInterruptFlag_.compare_exchange_strong(oldStatus, ReleasePagesStatus::RELEASING_PAGES)) {
1985         ASSERT(this->GetSettings()->GCWorkersCount() != 0);
1986         if (!this->GetWorkersTaskPool()->AddTask(GCWorkersTaskTypes::TASK_RETURN_FREE_PAGES_TO_OS)) {
1987             PoolManager::GetMmapMemPool()->ReleaseFreePagesToOS();
1988             releasePagesInterruptFlag_ = ReleasePagesStatus::FINISHED;
1989         }
1990     }
1991 }
1992 
1993 template <class LanguageConfig>
HaveEnoughSpaceToMove(const CollectionSet & collectibleRegions)1994 bool G1GC<LanguageConfig>::HaveEnoughSpaceToMove(const CollectionSet &collectibleRegions)
1995 {
1996     // extra regions are required because workers concurrenly evacuate objects to several regions
1997     size_t extraRegions =
1998         this->GetPandaVm()->SupportGCSinglePassCompaction() ? this->GetSettings()->GCWorkersCount() : 0;
1999     return HaveEnoughRegionsToMove(collectibleRegions.Movable().size() + extraRegions);
2000 }
2001 
2002 template <class LanguageConfig>
HaveEnoughRegionsToMove(size_t num)2003 bool G1GC<LanguageConfig>::HaveEnoughRegionsToMove(size_t num)
2004 {
2005     return GetG1ObjectAllocator()->HaveTenuredSize(num) && GetG1ObjectAllocator()->HaveFreeRegions(num);
2006 }
2007 
2008 template <class LanguageConfig>
OnThreadTerminate(ManagedThread * thread,mem::BuffersKeepingFlag keepBuffers)2009 void G1GC<LanguageConfig>::OnThreadTerminate(ManagedThread *thread, mem::BuffersKeepingFlag keepBuffers)
2010 {
2011     InternalAllocatorPtr allocator = this->GetInternalAllocator();
2012     // The method must be called while the lock which guards thread/coroutine list is hold
2013     LOG(DEBUG, GC) << "Call OnThreadTerminate";
2014     PandaVector<ObjectHeader *> *preBuff = nullptr;
2015     if (keepBuffers == mem::BuffersKeepingFlag::KEEP) {
2016         preBuff = allocator->New<PandaVector<ObjectHeader *>>(*thread->GetPreBuff());
2017         thread->GetPreBuff()->clear();
2018     } else {  // keep_buffers == mem::BuffersKeepingFlag::DELETE
2019         preBuff = thread->MovePreBuff();
2020     }
2021     ASSERT(preBuff != nullptr);
2022     {
2023         os::memory::LockHolder lock(satbAndNewobjBufLock_);
2024         satbBuffList_.push_back(preBuff);
2025     }
2026     {
2027         auto *localBuffer = thread->GetG1PostBarrierBuffer();
2028         ASSERT(localBuffer != nullptr);
2029         if (!localBuffer->IsEmpty()) {
2030             auto *tempBuffer = allocator->New<PandaVector<mem::CardTable::CardPtr>>();
2031             while (!localBuffer->IsEmpty()) {
2032                 tempBuffer->push_back(localBuffer->Pop());
2033             }
2034             updateRemsetWorker_->AddPostBarrierBuffer(tempBuffer);
2035         }
2036         if (keepBuffers == mem::BuffersKeepingFlag::DELETE) {
2037             thread->ResetG1PostBarrierBuffer();
2038             allocator->Delete(localBuffer);
2039         }
2040     }
2041 }
2042 
2043 template <class LanguageConfig>
OnThreadCreate(ManagedThread * thread)2044 void G1GC<LanguageConfig>::OnThreadCreate(ManagedThread *thread)
2045 {
2046     // Any access to other threads' data (including MAIN's) might cause a race here
2047     // so don't do this please.
2048     thread->SetPreWrbEntrypoint(reinterpret_cast<void *>(currentPreWrbEntrypoint_));
2049 }
2050 
2051 template <class LanguageConfig>
PreZygoteFork()2052 void G1GC<LanguageConfig>::PreZygoteFork()
2053 {
2054     GC::PreZygoteFork();
2055     this->DestroyWorkersTaskPool();
2056     this->DisableWorkerThreads();
2057     updateRemsetWorker_->DestroyWorker();
2058     // don't use thread while we are in zygote
2059     updateRemsetWorker_->SetUpdateConcurrent(false);
2060 }
2061 
2062 template <class LanguageConfig>
PostZygoteFork()2063 void G1GC<LanguageConfig>::PostZygoteFork()
2064 {
2065     this->EnableWorkerThreads();
2066     this->CreateWorkersTaskPool();
2067     GC::PostZygoteFork();
2068     // use concurrent-option after zygote
2069     updateRemsetWorker_->SetUpdateConcurrent(this->GetSettings()->G1EnableConcurrentUpdateRemset());
2070     updateRemsetWorker_->CreateWorker();
2071 }
2072 
2073 template <class LanguageConfig>
DrainSatb(GCAdaptiveStack * objectStack)2074 void G1GC<LanguageConfig>::DrainSatb(GCAdaptiveStack *objectStack)
2075 {
2076     ScopedTiming scopedTiming(__FUNCTION__, *this->GetTiming());
2077     // Process satb buffers of the active threads
2078     auto callback = [this, objectStack](ManagedThread *thread) {
2079         // Acquire lock here to avoid data races with the threads
2080         // which are terminating now.
2081         // Data race is happens in thread.pre_buf_. The terminating thread may
2082         // release own pre_buf_ while GC thread iterates over threads and gets theirs
2083         // pre_buf_.
2084         os::memory::LockHolder lock(satbAndNewobjBufLock_);
2085         auto preBuff = thread->GetPreBuff();
2086         if (preBuff == nullptr) {
2087             // This can happens when the thread gives us own satb_buffer but
2088             // doesn't unregister from ThreadManaged.
2089             // At this perion GC can happen and we get pre_buff null here.
2090             return true;
2091         }
2092         for (auto obj : *preBuff) {
2093             if (marker_.MarkIfNotMarked(obj)) {
2094                 objectStack->PushToStack(RootType::SATB_BUFFER, obj);
2095             }
2096         }
2097         preBuff->clear();
2098         return true;
2099     };
2100     this->GetPandaVm()->GetThreadManager()->EnumerateThreads(callback);
2101 
2102     // Process satb buffers of the terminated threads
2103     os::memory::LockHolder lock(satbAndNewobjBufLock_);
2104     for (auto objVector : satbBuffList_) {
2105         ASSERT(objVector != nullptr);
2106         for (auto obj : *objVector) {
2107             if (marker_.MarkIfNotMarked(obj)) {
2108                 objectStack->PushToStack(RootType::SATB_BUFFER, obj);
2109             }
2110         }
2111         this->GetInternalAllocator()->Delete(objVector);
2112     }
2113     satbBuffList_.clear();
2114     for (auto obj : newobjBuffer_) {
2115         if (marker_.MarkIfNotMarked(obj)) {
2116             objectStack->PushToStack(RootType::SATB_BUFFER, obj);
2117         }
2118     }
2119     newobjBuffer_.clear();
2120 }
2121 
2122 template <class LanguageConfig>
HandlePendingDirtyCards()2123 void G1GC<LanguageConfig>::HandlePendingDirtyCards()
2124 {
2125     ScopedTiming t(__FUNCTION__, *this->GetTiming());
2126     updateRemsetWorker_->DrainAllCards(&dirtyCards_);
2127     std::for_each(dirtyCards_.cbegin(), dirtyCards_.cend(), [](auto card) { card->UnMark(); });
2128 }
2129 
2130 template <class LanguageConfig>
ReenqueueDirtyCards()2131 void G1GC<LanguageConfig>::ReenqueueDirtyCards()
2132 {
2133     ScopedTiming t(__FUNCTION__, *this->GetTiming());
2134     os::memory::LockHolder lock(queueLock_);
2135     std::for_each(dirtyCards_.cbegin(), dirtyCards_.cend(), [this](auto card) {
2136         card->Mark();
2137         updatedRefsQueue_->push_back(card);
2138     });
2139     dirtyCards_.clear();
2140 }
2141 
2142 template <class LanguageConfig>
ClearSatb()2143 void G1GC<LanguageConfig>::ClearSatb()
2144 {
2145     ScopedTiming scopedTiming(__FUNCTION__, *this->GetTiming());
2146     // Acquire lock here to avoid data races with the threads
2147     // which are terminating now.
2148     // Data race is happens in thread.pre_buf_. The terminating thread may
2149     // release own pre_buf_ while GC thread iterates over threads and gets theirs
2150     // pre_buf_.
2151     os::memory::LockHolder lock(satbAndNewobjBufLock_);
2152     // Process satb buffers of the active threads
2153     auto threadCallback = [](ManagedThread *thread) {
2154         auto preBuff = thread->GetPreBuff();
2155         if (preBuff != nullptr) {
2156             preBuff->clear();
2157         }
2158         return true;
2159     };
2160     this->GetPandaVm()->GetThreadManager()->EnumerateThreads(threadCallback);
2161 
2162     // Process satb buffers of the terminated threads
2163     for (auto objVector : satbBuffList_) {
2164         this->GetInternalAllocator()->Delete(objVector);
2165     }
2166     satbBuffList_.clear();
2167     newobjBuffer_.clear();
2168 }
2169 
2170 template <class LanguageConfig>
2171 template <class Visitor>
VisitRemSets(const Visitor & visitor)2172 void G1GC<LanguageConfig>::VisitRemSets(const Visitor &visitor)
2173 {
2174     GCScope<TRACE_TIMING> visitRemsetScope(__FUNCTION__, this);
2175 
2176     ASSERT(uniqueCardsInitialized_);
2177     // Iterate over stored references to the collection set
2178     for (auto &entryVector : uniqueRefsFromRemsets_) {
2179         for (auto &entry : *entryVector) {
2180             ObjectHeader *object = entry.GetObject();
2181             uint32_t offset = entry.GetReferenceOffset();
2182             visitor(object, ObjectAccessor::GetObject(object, offset), offset);
2183         }
2184     }
2185 }
2186 
2187 template <class LanguageConfig>
2188 template <class Visitor>
UpdateRefsFromRemSets(const Visitor & visitor)2189 void G1GC<LanguageConfig>::UpdateRefsFromRemSets(const Visitor &visitor)
2190 {
2191     auto fieldVisitor = [this, &visitor](ObjectHeader *object, ObjectHeader *field, uint32_t offset,
2192                                          [[maybe_unused]] bool isVolatile) {
2193         if (!InGCSweepRange(field)) {
2194             return true;
2195         }
2196         visitor(object, ObjectAccessor::GetObject(object, offset), offset);
2197         return true;
2198     };
2199     auto refsChecker = [this, &fieldVisitor](Region *region, const MemRange &memRange) {
2200         IterateOverRefsInMemRange(memRange, region, fieldVisitor);
2201         return true;
2202     };
2203     CacheRefsFromRemsets(refsChecker);
2204 }
2205 
2206 template <class LanguageConfig>
IsCollectionSetFullyPromoted() const2207 bool G1GC<LanguageConfig>::IsCollectionSetFullyPromoted() const
2208 {
2209     if (!collectionSet_.Tenured().empty()) {
2210         return false;
2211     }
2212     for (Region *region : collectionSet_.Young()) {
2213         if (!region->HasFlag(RegionFlag::IS_PROMOTED)) {
2214             return false;
2215         }
2216     }
2217     return true;
2218 }
2219 
2220 template <class LanguageConfig>
CacheRefsFromRemsets(const MemRangeRefsChecker & refsChecker)2221 void G1GC<LanguageConfig>::CacheRefsFromRemsets(const MemRangeRefsChecker &refsChecker)
2222 {
2223     GCScope<TRACE_TIMING> cacheRefsFromRemsetScope(__FUNCTION__, this);
2224     // Collect only unique objects to not proceed them more than once.
2225     ASSERT(!uniqueCardsInitialized_);
2226 
2227     size_t remsetSize = 0;
2228     auto visitor = [&remsetSize, &refsChecker](Region *r, const MemRange &range) {
2229         remsetSize++;
2230         return refsChecker(r, range);
2231     };
2232 
2233     GlobalRemSet globalRemSet;
2234     globalRemSet.ProcessRemSets(collectionSet_, RemsetRegionPredicate, visitor);
2235 
2236     analytics_.ReportRemsetSize(remsetSize, GetUniqueRemsetRefsCount());
2237 
2238     if (!this->IsFullGC()) {
2239         auto dirtyCardsCount = dirtyCards_.size();
2240         analytics_.ReportScanDirtyCardsStart(ark::time::GetCurrentTimeInNanos());
2241         CacheRefsFromDirtyCards(globalRemSet, refsChecker);
2242         analytics_.ReportScanDirtyCardsEnd(ark::time::GetCurrentTimeInNanos(), dirtyCardsCount);
2243 #ifndef NDEBUG
2244         uniqueCardsInitialized_ = true;
2245 #endif  // NDEBUG
2246     }
2247 }
2248 
2249 template <class LanguageConfig>
2250 template <typename Visitor>
CacheRefsFromDirtyCards(GlobalRemSet & globalRemSet,Visitor visitor)2251 void G1GC<LanguageConfig>::CacheRefsFromDirtyCards(GlobalRemSet &globalRemSet, Visitor visitor)
2252 {
2253     ScopedTiming t(__FUNCTION__, *this->GetTiming());
2254     auto cardTable = this->GetCardTable();
2255     for (auto it = dirtyCards_.cbegin(); it != dirtyCards_.cend();) {
2256         auto range = cardTable->GetMemoryRange(*it);
2257         auto addr = range.GetStartAddress();
2258         ASSERT_DO(IsHeapSpace(PoolManager::GetMmapMemPool()->GetSpaceTypeForAddr(ToVoidPtr(addr))),
2259                   std::cerr << "Invalid space type for the " << addr << std::endl);
2260         auto region = ark::mem::AddrToRegion(ToVoidPtr(addr));
2261         if (!RemsetRegionPredicate(region)) {
2262             it = dirtyCards_.erase(it);
2263             continue;
2264         }
2265 
2266         auto allCrossRegionRefsProcessed = globalRemSet.IterateOverUniqueRange(region, range, visitor);
2267         if (allCrossRegionRefsProcessed) {
2268             it = dirtyCards_.erase(it);
2269             continue;
2270         }
2271         ++it;
2272     }
2273 }
2274 
2275 template <class LanguageConfig>
RestoreYoungCards(const CollectionSet & collectionSet)2276 void G1GC<LanguageConfig>::RestoreYoungCards(const CollectionSet &collectionSet)
2277 {
2278     CardTable *cardTable = this->GetCardTable();
2279     for (Region *region : collectionSet.Young()) {
2280         cardTable->MarkCardsAsYoung(MemRange(region->Begin(), region->End()));
2281     }
2282 }
2283 
2284 template <class LanguageConfig>
ClearYoungCards(const CollectionSet & collectionSet)2285 void G1GC<LanguageConfig>::ClearYoungCards(const CollectionSet &collectionSet)
2286 {
2287     auto *cardTable = this->GetCardTable();
2288     for (Region *region : collectionSet.Young()) {
2289         cardTable->ClearCardRange(ToUintPtr(region), ToUintPtr(region) + DEFAULT_REGION_SIZE);
2290     }
2291 }
2292 
2293 template <class LanguageConfig>
ClearTenuredCards(const CollectionSet & collectionSet)2294 void G1GC<LanguageConfig>::ClearTenuredCards(const CollectionSet &collectionSet)
2295 {
2296     auto *cardTable = this->GetCardTable();
2297     for (Region *region : collectionSet.Tenured()) {
2298         cardTable->ClearCardRange(ToUintPtr(region), ToUintPtr(region) + DEFAULT_REGION_SIZE);
2299     }
2300 }
2301 
2302 template <class LanguageConfig>
ClearRefsFromRemsetsCache()2303 void G1GC<LanguageConfig>::ClearRefsFromRemsetsCache()
2304 {
2305     ASSERT(!uniqueRefsFromRemsets_.empty());
2306     // Resize list of unique refs from remset to 1, to reduce memory usage
2307     size_t elemetsToRemove = uniqueRefsFromRemsets_.size() - 1;
2308     for (size_t i = 0; i < elemetsToRemove; i++) {
2309         RefVector *entry = uniqueRefsFromRemsets_.back();
2310         this->GetInternalAllocator()->Delete(entry);
2311         uniqueRefsFromRemsets_.pop_back();
2312     }
2313     ASSERT(uniqueRefsFromRemsets_.size() == 1);
2314     uniqueRefsFromRemsets_.front()->clear();
2315     ASSERT(uniqueRefsFromRemsets_.front()->capacity() == MAX_REFS);
2316 #ifndef NDEBUG
2317     uniqueCardsInitialized_ = false;
2318 #endif  // NDEBUG
2319 }
2320 
2321 template <class LanguageConfig>
ActualizeRemSets()2322 void G1GC<LanguageConfig>::ActualizeRemSets()
2323 {
2324     ScopedTiming t(__FUNCTION__, *this->GetTiming());
2325     auto *objectAllocator = this->GetG1ObjectAllocator();
2326     // Invalidate regions from collection set in all remsets
2327     for (Region *region : collectionSet_.Young()) {
2328         if (!region->HasFlag(RegionFlag::IS_PROMOTED)) {
2329             RemSet<>::template InvalidateRegion<false>(region);
2330         } else {
2331             objectAllocator->AddPromotedRegionToQueueIfPinned(region);
2332             region->RmvFlag(RegionFlag::IS_PROMOTED);
2333         }
2334     }
2335     for (Region *region : collectionSet_.Tenured()) {
2336         RemSet<>::template InvalidateRegion<false>(region);
2337     }
2338 }
2339 
2340 template <class LanguageConfig>
ShouldRunTenuredGC(const GCTask & task)2341 bool G1GC<LanguageConfig>::ShouldRunTenuredGC(const GCTask &task)
2342 {
2343     return this->IsOnPygoteFork() || task.reason == GCTaskCause::HEAP_USAGE_THRESHOLD_CAUSE ||
2344            task.reason == GCTaskCause::STARTUP_COMPLETE_CAUSE;
2345 }
2346 
2347 template <class LanguageConfig>
OnWaitForIdleFail()2348 void G1GC<LanguageConfig>::OnWaitForIdleFail()
2349 {
2350     if (this->GetGCPhase() == GCPhase::GC_PHASE_MARK) {
2351         // Atomic with release order reason: write to this variable should become visible in concurrent marker check
2352         interruptConcurrentFlag_.store(true, std::memory_order_release);
2353         if (this->GetSettings()->G1EnablePauseTimeGoal()) {
2354             os::memory::LockHolder lh(concurrentMarkMutex_);
2355             concurrentMarkCondVar_.Signal();
2356         }
2357     }
2358 }
2359 
2360 template <class LanguageConfig>
PostponeGCStart()2361 void G1GC<LanguageConfig>::PostponeGCStart()
2362 {
2363     regionGarbageRateThreshold_ = 0;
2364     g1PromotionRegionAliveRate_ = 0;
2365     GC::PostponeGCStart();
2366 }
2367 
2368 template <class LanguageConfig>
PostponeGCEnd()2369 void G1GC<LanguageConfig>::PostponeGCEnd()
2370 {
2371     ASSERT(!this->IsPostponeEnabled() || (regionGarbageRateThreshold_ == 0 && g1PromotionRegionAliveRate_ == 0));
2372     regionGarbageRateThreshold_ = this->GetSettings()->G1RegionGarbageRateThreshold();
2373     g1PromotionRegionAliveRate_ = this->GetSettings()->G1PromotionRegionAliveRate();
2374     GC::PostponeGCEnd();
2375 }
2376 
2377 template <class LanguageConfig>
IsPostponeGCSupported() const2378 bool G1GC<LanguageConfig>::IsPostponeGCSupported() const
2379 {
2380     return true;
2381 }
2382 
2383 template <class LanguageConfig>
GetMaxMixedRegionsCount()2384 size_t G1GC<LanguageConfig>::GetMaxMixedRegionsCount()
2385 {
2386     return this->GetG1ObjectAllocator()->GetMaxYoungRegionsCount() + numberOfMixedTenuredRegions_;
2387 }
2388 
2389 template <class LanguageConfig>
PrepareYoungRegionsForFullGC(const CollectionSet & collectionSet)2390 void G1GC<LanguageConfig>::PrepareYoungRegionsForFullGC(const CollectionSet &collectionSet)
2391 {
2392     BuildCrossYoungRemSets(collectionSet.Young());
2393     ClearYoungCards(collectionSet);
2394 }
2395 
2396 template <class LanguageConfig>
RestoreYoungRegionsAfterFullGC(const CollectionSet & collectionSet)2397 void G1GC<LanguageConfig>::RestoreYoungRegionsAfterFullGC(const CollectionSet &collectionSet)
2398 {
2399     RestoreYoungCards(collectionSet);
2400     for (Region *region : collectionSet.Young()) {
2401         RemSet<>::template InvalidateRefsFromRegion<false>(region);
2402     }
2403 }
2404 
2405 template <class LanguageConfig>
2406 template <typename Container>
BuildCrossYoungRemSets(const Container & young)2407 void G1GC<LanguageConfig>::BuildCrossYoungRemSets(const Container &young)
2408 {
2409     ScopedTiming scopedTiming(__FUNCTION__, *this->GetTiming());
2410     ASSERT(this->IsFullGC());
2411     auto allocator = this->GetG1ObjectAllocator();
2412     size_t regionSizeBits = ark::helpers::math::GetIntLog2(allocator->GetRegionSize());
2413     auto updateRemsets = [regionSizeBits](ObjectHeader *object, ObjectHeader *ref, size_t offset,
2414                                           [[maybe_unused]] bool isVolatile) {
2415         if (!IsSameRegion(object, ref, regionSizeBits) && !ObjectToRegion(ref)->IsYoung()) {
2416             RemSet<>::AddRefWithAddr<false>(object, offset, ref);
2417         }
2418         return true;
2419     };
2420     for (Region *region : young) {
2421         region->GetMarkBitmap()->IterateOverMarkedChunks([&updateRemsets](void *addr) {
2422             ObjectHelpers<LanguageConfig::LANG_TYPE>::template TraverseAllObjectsWithInfo<false>(
2423                 reinterpret_cast<ObjectHeader *>(addr), updateRemsets);
2424         });
2425     }
2426 }
2427 
2428 template <class LanguageConfig>
StartConcurrentScopeRoutine() const2429 void G1GC<LanguageConfig>::StartConcurrentScopeRoutine() const
2430 {
2431     updateRemsetWorker_->ResumeWorkerAfterGCPause();
2432 }
2433 
2434 template <class LanguageConfig>
EndConcurrentScopeRoutine() const2435 void G1GC<LanguageConfig>::EndConcurrentScopeRoutine() const
2436 {
2437     updateRemsetWorker_->SuspendWorkerForGCPause();
2438 }
2439 
2440 template <class LanguageConfig>
ComputeNewSize()2441 void G1GC<LanguageConfig>::ComputeNewSize()
2442 {
2443     if (this->GetSettings()->G1EnablePauseTimeGoal()) {
2444         auto desiredEdenLengthByPauseDelay = CalculateDesiredEdenLengthByPauseDelay();
2445         auto desiredEdenLengthByPauseDuration = CalculateDesiredEdenLengthByPauseDuration();
2446         auto desiredEdenLength = std::max(desiredEdenLengthByPauseDelay, desiredEdenLengthByPauseDuration);
2447         GetG1ObjectAllocator()->GetHeapSpace()->UpdateSize(desiredEdenLength * GetG1ObjectAllocator()->GetRegionSize());
2448         GetG1ObjectAllocator()->SetDesiredEdenLength(desiredEdenLength);
2449     } else {
2450         GenerationalGC<LanguageConfig>::ComputeNewSize();
2451     }
2452 }
2453 
2454 template <class LanguageConfig>
CalculateDesiredEdenLengthByPauseDelay()2455 size_t G1GC<LanguageConfig>::CalculateDesiredEdenLengthByPauseDelay()
2456 {
2457     auto delayBeforePause = g1PauseTracker_.MinDelayBeforeMaxPauseInMicros(ark::time::GetCurrentTimeInMicros());
2458     return static_cast<size_t>(ceil(analytics_.PredictAllocationRate() * delayBeforePause));
2459 }
2460 
2461 template <class LanguageConfig>
CalculateDesiredEdenLengthByPauseDuration()2462 size_t G1GC<LanguageConfig>::CalculateDesiredEdenLengthByPauseDuration()
2463 {
2464     // Calculate desired_eden_size according to pause time goal
2465     size_t minEdenLength = 1;
2466     size_t maxEdenLength =
2467         GetG1ObjectAllocator()->GetHeapSpace()->GetMaxYoungSize() / GetG1ObjectAllocator()->GetRegionSize();
2468 
2469     // Atomic with relaxed order reason: data race with no synchronization or ordering constraints imposed
2470     // on other reads or writes
2471     if (isMixedGcRequired_.load(std::memory_order_relaxed)) {
2472         auto oldCandidates = GetOldCollectionSetCandidatesNumber();
2473         if (oldCandidates >= maxEdenLength) {
2474             // Schedule next mixed collections as often as possible to maximize old regions collection
2475             return 1;
2476         }
2477         maxEdenLength -= oldCandidates;
2478     }
2479 
2480     auto maxPause = this->GetSettings()->GetG1MaxGcPauseInMillis() * ark::os::time::MILLIS_TO_MICRO;
2481     auto edenLengthPredicate = [this, maxPause](size_t edenLength) {
2482         if (!HaveEnoughRegionsToMove(edenLength)) {
2483             return false;
2484         }
2485         auto pauseTime = analytics_.PredictYoungCollectionTimeInMicros(edenLength);
2486         return pauseTime <= maxPause;
2487     };
2488     if (!edenLengthPredicate(minEdenLength)) {
2489         return minEdenLength;
2490     }
2491     if (edenLengthPredicate(maxEdenLength)) {
2492         return maxEdenLength;
2493     }
2494     auto delta = (maxEdenLength - minEdenLength) / 2U;
2495     while (delta > 0) {
2496         auto edenLength = minEdenLength + delta;
2497         if (edenLengthPredicate(edenLength)) {
2498             minEdenLength = edenLength;
2499         } else {
2500             maxEdenLength = edenLength;
2501         }
2502         ASSERT(minEdenLength < maxEdenLength);
2503         delta = (maxEdenLength - minEdenLength) / 2U;
2504     }
2505     return minEdenLength;
2506 }
2507 
2508 template <class LanguageConfig>
ConcurentMarkImpl(GCMarkingStackType * objectsStack)2509 NO_THREAD_SAFETY_ANALYSIS void G1GC<LanguageConfig>::ConcurentMarkImpl(GCMarkingStackType *objectsStack)
2510 {
2511     {
2512         ScopedTiming t("VisitClassRoots", *this->GetTiming());
2513         this->VisitClassRoots([this, objectsStack](const GCRoot &gcRoot) {
2514             if (concMarker_.MarkIfNotMarked(gcRoot.GetObjectHeader())) {
2515                 ASSERT(gcRoot.GetObjectHeader() != nullptr);
2516                 objectsStack->PushToStack(RootType::ROOT_CLASS, gcRoot.GetObjectHeader());
2517             } else {
2518                 LOG_DEBUG_GC << "Skip root: " << gcRoot.GetObjectHeader();
2519             }
2520         });
2521     }
2522     {
2523         ScopedTiming t("VisitInternalStringTable", *this->GetTiming());
2524         this->GetPandaVm()->VisitStringTable(
2525             [this, objectsStack](ObjectHeader *str) {
2526                 if (concMarker_.MarkIfNotMarked(str)) {
2527                     ASSERT(str != nullptr);
2528                     objectsStack->PushToStack(RootType::STRING_TABLE, str);
2529                 }
2530             },
2531             VisitGCRootFlags::ACCESS_ROOT_ALL | VisitGCRootFlags::START_RECORDING_NEW_ROOT);
2532     }
2533     // Atomic with acquire order reason: load to this variable should become visible
2534     while (!objectsStack->Empty() && !interruptConcurrentFlag_.load(std::memory_order_acquire)) {
2535         auto *object = this->PopObjectFromStack(objectsStack);
2536         ASSERT(concMarker_.IsMarked(object));
2537         ValidateObject(nullptr, object);
2538         auto *objectClass = object->template ClassAddr<BaseClass>();
2539         // We need annotation here for the FullMemoryBarrier used in InitializeClassByIdEntrypoint
2540         TSAN_ANNOTATE_HAPPENS_AFTER(objectClass);
2541         LOG_DEBUG_GC << "Current object: " << GetDebugInfoAboutObject(object);
2542 
2543         ASSERT(!object->IsForwarded());
2544         CalcLiveBytesNotAtomicallyMarkPreprocess(object, objectClass);
2545         concMarker_.MarkInstance(objectsStack, object, objectClass);
2546     }
2547 }
2548 
2549 template <class LanguageConfig>
Trigger(PandaUniquePtr<GCTask> task)2550 bool G1GC<LanguageConfig>::Trigger(PandaUniquePtr<GCTask> task)
2551 {
2552     if (this->GetSettings()->G1EnablePauseTimeGoal() &&
2553         g1PauseTracker_.MinDelayBeforeMaxPauseInMicros(ark::time::GetCurrentTimeInMicros()) > 0) {
2554         return false;
2555     }
2556     return GenerationalGC<LanguageConfig>::Trigger(std::move(task));
2557 }
2558 
2559 template <class LanguageConfig>
GetUniqueRemsetRefsCount() const2560 size_t G1GC<LanguageConfig>::GetUniqueRemsetRefsCount() const
2561 {
2562     size_t count = 0;
2563     for (const auto *v : uniqueRefsFromRemsets_) {
2564         count += v->size();
2565     }
2566     return count;
2567 }
2568 
2569 TEMPLATE_CLASS_LANGUAGE_CONFIG(G1GC);
2570 TEMPLATE_CLASS_LANGUAGE_CONFIG(G1GCConcurrentMarker);
2571 TEMPLATE_CLASS_LANGUAGE_CONFIG(G1GCMixedMarker);
2572 TEMPLATE_CLASS_LANGUAGE_CONFIG(G1GCPauseMarker);
2573 
2574 }  // namespace ark::mem
2575