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