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 ®ionInfo)
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 *> ®ions) {
361 if constexpr (CONCURRENTLY) {
362 updateRemsetWorker_->InvalidateRegions(®ions);
363 } else {
364 updateRemsetWorker_->GCInvalidateRegions(®ions);
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 ®ion : 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