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