• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 "common_components/heap/collector/marking_collector.h"
17 
18 #include <new>
19 #include <iomanip>
20 
21 #include "common_components/heap/allocator/alloc_buffer.h"
22 #include "common_components/heap/collector/heuristic_gc_policy.h"
23 #include "common_interfaces/base/runtime_param.h"
24 #include <string>
25 
26 namespace common {
27 const size_t MarkingCollector::MAX_MARKING_WORK_SIZE = 16; // fork task if bigger
28 const size_t MarkingCollector::MIN_MARKING_WORK_SIZE = 8;  // forbid forking task if smaller
29 
VisitRoots(const RefFieldVisitor & visitor)30 void StaticRootTable::VisitRoots(const RefFieldVisitor& visitor)
31 {
32     std::lock_guard<std::mutex> lock(gcRootsLock_);
33     uint32_t gcRootsSize = 0;
34     std::unordered_set<RefField<>*> visitedSet;
35     for (auto iter = gcRootsBuckets_.begin(); iter != gcRootsBuckets_.end(); iter++) {
36         gcRootsSize = iter->second;
37         StaticRootArray* array = iter->first;
38         for (uint32_t i = 0; i < gcRootsSize; i++) {
39             RefField<>* root = array->content[i];
40             // make sure to visit each static root only once time.
41             if (visitedSet.find(root) != visitedSet.end()) {
42                 continue;
43             }
44             visitedSet.insert(root);
45             visitor(*root);
46         }
47     }
48 }
49 
50 class ConcurrentMarkingTask : public common::Task {
51 public:
ConcurrentMarkingTask(uint32_t id,MarkingCollector & tc,Taskpool * pool,TaskPackMonitor & monitor,GlobalWorkStackQueue & globalQueue)52     ConcurrentMarkingTask(uint32_t id, MarkingCollector &tc, Taskpool *pool, TaskPackMonitor &monitor,
53                           GlobalWorkStackQueue &globalQueue)
54         : Task(id), collector_(tc), threadPool_(pool), monitor_(monitor), globalQueue_(globalQueue)
55     {}
56 
57     // single work task without thread pool
ConcurrentMarkingTask(uint32_t id,MarkingCollector & tc,TaskPackMonitor & monitor,GlobalWorkStackQueue & globalQueue)58     ConcurrentMarkingTask(uint32_t id, MarkingCollector& tc, TaskPackMonitor &monitor,
59                           GlobalWorkStackQueue &globalQueue)
60         : Task(id), collector_(tc), threadPool_(nullptr), monitor_(monitor), globalQueue_(globalQueue)
61     {}
62 
~ConcurrentMarkingTask()63     ~ConcurrentMarkingTask() override
64     {
65         threadPool_ = nullptr;
66     }
67 
68     // run concurrent marking task.
Run(uint32_t threadIndex)69     bool Run([[maybe_unused]] uint32_t threadIndex) override
70     {
71         while (true) {
72             WorkStack workStack = globalQueue_.PopWorkStack();
73             if (workStack.empty()) {
74                 break;
75             }
76             collector_.ProcessMarkStack(threadIndex, threadPool_, workStack, globalQueue_);
77         }
78         monitor_.NotifyFinishOne();
79         return true;
80     }
81 
82 private:
83     MarkingCollector &collector_;
84     Taskpool *threadPool_;
85     TaskPackMonitor &monitor_;
86     GlobalWorkStackQueue &globalQueue_;
87 };
88 
89 class ClearWeakStackTask : public common::Task {
90 public:
ClearWeakStackTask(uint32_t id,MarkingCollector & tc,Taskpool * pool,TaskPackMonitor & monitor,GlobalWeakStackQueue & globalQueue)91     ClearWeakStackTask(uint32_t id, MarkingCollector &tc, Taskpool *pool, TaskPackMonitor &monitor,
92                           GlobalWeakStackQueue &globalQueue)
93         : Task(id), collector_(tc), threadPool_(pool), monitor_(monitor), globalQueue_(globalQueue)
94     {}
95 
96     // single work task without thread pool
ClearWeakStackTask(uint32_t id,MarkingCollector & tc,TaskPackMonitor & monitor,GlobalWeakStackQueue & globalQueue)97     ClearWeakStackTask(uint32_t id, MarkingCollector& tc, TaskPackMonitor &monitor,
98                           GlobalWeakStackQueue &globalQueue)
99         : Task(id), collector_(tc), threadPool_(nullptr), monitor_(monitor), globalQueue_(globalQueue)
100     {}
101 
~ClearWeakStackTask()102     ~ClearWeakStackTask() override
103     {
104         threadPool_ = nullptr;
105     }
106 
107     // run concurrent marking task.
Run(uint32_t threadIndex)108     bool Run([[maybe_unused]] uint32_t threadIndex) override
109     {
110         while (true) {
111             WeakStack weakStack = globalQueue_.PopWorkStack();
112             if (weakStack.empty()) {
113                 break;
114             }
115             collector_.ProcessWeakStack(weakStack);
116         }
117         monitor_.NotifyFinishOne();
118         return true;
119     }
120 
121 private:
122     MarkingCollector &collector_;
123     Taskpool *threadPool_;
124     TaskPackMonitor &monitor_;
125     GlobalWeakStackQueue &globalQueue_;
126 };
127 
TryForkTask(Taskpool * threadPool,WorkStack & workStack,GlobalWorkStackQueue & globalQueue)128 void MarkingCollector::TryForkTask(Taskpool *threadPool, WorkStack &workStack, GlobalWorkStackQueue &globalQueue)
129 {
130     size_t size = workStack.size();
131     if (size > MIN_MARKING_WORK_SIZE) {
132         bool doFork = false;
133         size_t newSize = 0;
134         if (size > MAX_MARKING_WORK_SIZE) {
135             newSize = size >> 1; // give 1/2 the stack to the thread pool as a new work task
136             doFork = true;
137         } else if (size > MIN_MARKING_WORK_SIZE) {
138             constexpr uint8_t shiftForEight = 3;
139             newSize = size >> shiftForEight; // give 1/8 the stack to the thread pool as a new work task
140             doFork = true;
141         }
142 
143         if (doFork) {
144             WorkStackBuf *hSplit = workStack.split(newSize);
145             globalQueue.AddWorkStack(WorkStack(hSplit));
146         }
147     }
148 }
149 
ProcessWeakStack(WeakStack & weakStack)150 void MarkingCollector::ProcessWeakStack(WeakStack &weakStack)
151 {
152     while (!weakStack.empty()) {
153         auto [fieldPointer, offset] = *weakStack.back();
154         weakStack.pop_back();
155         ASSERT_LOGF(offset % sizeof(RefField<>) == 0, "offset is not aligned");
156 
157         RefField<> &field = reinterpret_cast<RefField<>&>(*fieldPointer);
158         RefField<> oldField(field);
159 
160         if (!Heap::IsTaggedObject(oldField.GetFieldValue())) {
161             continue;
162         }
163         BaseObject* targetObj = oldField.GetTargetObject();
164         DCHECK_CC(Heap::IsHeapAddress(targetObj));
165 
166         auto targetRegion = RegionDesc::GetAliveRegionDescAt(reinterpret_cast<MAddress>(targetObj));
167         if (targetRegion->IsMarkedObject(targetObj) || targetRegion->IsNewObjectSinceMarking(targetObj)) {
168             continue;
169         }
170 
171         BaseObject* obj = reinterpret_cast<BaseObject*>(reinterpret_cast<uintptr_t>(&field) - offset);
172         if (RegionDesc::GetAliveRegionType(reinterpret_cast<MAddress>(obj)) == RegionDesc::RegionType::FROM_REGION) {
173             BaseObject* toObj = obj->GetForwardingPointer();
174 
175             // Make sure even the object that contains the weak reference is trimed before forwarding, the weak ref
176             // field is still within the object
177             if (toObj != nullptr && offset < obj->GetSizeForwarded()) {
178                 RefField<>& toField = *reinterpret_cast<RefField<>*>(reinterpret_cast<uintptr_t>(toObj) + offset);
179                 toField.ClearRef(oldField.GetFieldValue());
180             }
181         }
182         field.ClearRef(oldField.GetFieldValue());
183     }
184 }
185 
ProcessMarkStack(uint32_t threadIndex,Taskpool * threadPool,WorkStack & workStack,GlobalWorkStackQueue & globalQueue)186 void MarkingCollector::ProcessMarkStack([[maybe_unused]] uint32_t threadIndex, Taskpool *threadPool,
187                                         WorkStack &workStack, GlobalWorkStackQueue &globalQueue)
188 {
189     size_t nNewlyMarked = 0;
190     WeakStack weakStack;
191     auto visitor = CreateMarkingObjectRefFieldsVisitor(&workStack, &weakStack);
192     WorkStack remarkStack;
193     auto fetchFromSatbBuffer = [this, &workStack, &remarkStack]() {
194         SatbBuffer::Instance().TryFetchOneRetiredNode(remarkStack);
195         while (!remarkStack.empty()) {
196             BaseObject *obj = remarkStack.back();
197             remarkStack.pop_back();
198             if (Heap::IsHeapAddress(obj) && (!MarkObject(obj))) {
199                 workStack.push_back(obj);
200                 DLOG(TRACE, "tracing take from satb buffer: obj %p", obj);
201             }
202         }
203     };
204     size_t iterationCnt = 0;
205     constexpr size_t maxIterationLoopNum = 1000;
206     // loop until work stack empty.
207     do {
208         for (;;) {
209             ++nNewlyMarked;
210             if (workStack.empty()) {
211                 break;
212             }
213             // get next object from work stack.
214             BaseObject *obj = workStack.back();
215             workStack.pop_back();
216             auto region = RegionDesc::GetAliveRegionDescAt(reinterpret_cast<MAddress>((void *)obj));
217             region->AddLiveByteCount(obj->GetSize());
218             [[maybe_unused]] auto beforeSize = workStack.count();
219             MarkingObjectRefFields(obj, &visitor);
220             DLOG(TRACE, "[tracing] visit finished, workstack size: before=%d, after=%d, newly added=%d", beforeSize,
221                  workStack.count(), workStack.count() - beforeSize);
222             // try to fork new task if needed.
223             if (threadPool != nullptr) {
224                 TryForkTask(threadPool, workStack, globalQueue);
225             }
226         }
227 
228         // Try some task from satb buffer, bound the loop to make sure it converges in time
229         if (++iterationCnt < maxIterationLoopNum) {
230             fetchFromSatbBuffer();
231             if (workStack.empty()) {
232                 fetchFromSatbBuffer();
233             }
234         }
235     } while (!workStack.empty());
236     // newly marked statistics.
237     markedObjectCount_.fetch_add(nNewlyMarked, std::memory_order_relaxed);
238     MergeWeakStack(weakStack);
239 }
240 
MergeWeakStack(WeakStack & weakStack)241 void MarkingCollector::MergeWeakStack(WeakStack& weakStack)
242 {
243     std::lock_guard<std::mutex> lock(weakStackLock_);
244 
245     // Preprocess the weak stack to minimize work during STW remark.
246     while (!weakStack.empty()) {
247         auto tuple = weakStack.back();
248         weakStack.pop_back();
249 
250         auto [weakFieldPointer, _] = *tuple;
251         RefField<> oldField(*weakFieldPointer);
252 
253         if (!Heap::IsTaggedObject(oldField.GetFieldValue())) {
254             continue;
255         }
256         auto obj = oldField.GetTargetObject();
257         DCHECK_CC(Heap::IsHeapAddress(obj));
258 
259         auto region = RegionDesc::GetAliveRegionDescAt(reinterpret_cast<MAddress>(obj));
260         if (region->IsNewObjectSinceMarking(obj) || region->IsMarkedObject(obj)) {
261             continue;
262         }
263 
264         globalWeakStack_.push_back(tuple);
265     }
266 }
267 
EnumConcurrencyModelRoots(RootSet & rootSet) const268 void MarkingCollector::EnumConcurrencyModelRoots(RootSet& rootSet) const
269 {
270     LOG_COMMON(FATAL) << "Unresolved fatal";
271     UNREACHABLE_CC();
272 }
273 
274 class MergeMutatorRootsScope {
275 public:
MergeMutatorRootsScope()276     MergeMutatorRootsScope() : manager_(&MutatorManager::Instance()), worldStopped_(manager_->WorldStopped())
277     {
278         if (!worldStopped_) {
279             manager_->MutatorManagementWLock();
280         }
281     }
282 
~MergeMutatorRootsScope()283     ~MergeMutatorRootsScope()
284     {
285         if (!worldStopped_) {
286             manager_->MutatorManagementWUnlock();
287         }
288     }
289 
290 private:
291     MutatorManager *manager_;
292     bool worldStopped_;
293 };
294 
MergeAllocBufferRoots(WorkStack & workStack)295 void MarkingCollector::MergeAllocBufferRoots(WorkStack& workStack)
296 {
297     // hold mutator list lock to freeze mutator liveness, otherwise may access dead mutator fatally
298     MergeMutatorRootsScope lockScope;
299     theAllocator_.VisitAllocBuffers([&workStack](AllocationBuffer &buffer) {
300         buffer.MarkStack([&workStack](BaseObject *o) { workStack.push_back(o); });
301     });
302 }
303 
TracingImpl(WorkStack & workStack,bool parallel,bool Remark)304 void MarkingCollector::TracingImpl(WorkStack& workStack, bool parallel, bool Remark)
305 {
306     OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, ("CMCGC::TracingImpl_" + std::to_string(workStack.count())).c_str(), "");
307     if (workStack.empty()) {
308         return;
309     }
310 
311     // enable parallel marking if we have thread pool.
312     Taskpool *threadPool = GetThreadPool();
313     ASSERT_LOGF(threadPool != nullptr, "thread pool is null");
314     if (parallel) { // parallel marking.
315         uint32_t parallelCount = 0;
316         // During the STW remark phase, Expect it to utilize all GC threads.
317         if (Remark) {
318             parallelCount = GetGCThreadCount(true);
319         } else {
320             parallelCount = GetGCThreadCount(true) - 1;
321         }
322         uint32_t threadCount = parallelCount + 1;
323         TaskPackMonitor monitor(parallelCount, parallelCount);
324         GlobalWorkStackQueue globalQueue;
325         for (uint32_t i = 0; i < parallelCount; ++i) {
326             threadPool->PostTask(std::make_unique<ConcurrentMarkingTask>(0, *this, threadPool, monitor, globalQueue));
327         }
328         if (!AddConcurrentTracingWork(workStack, globalQueue, static_cast<size_t>(threadCount))) {
329             ProcessMarkStack(0, threadPool, workStack, globalQueue);
330         }
331         while (true) {
332             WorkStack stack = globalQueue.DrainAllWorkStack();
333             if (stack.empty()) {
334                 break;
335             }
336             ProcessMarkStack(0, threadPool, stack, globalQueue);
337         }
338         globalQueue.NotifyFinish();
339         monitor.WaitAllFinished();
340     } else {
341         // serial marking with a single mark task.
342         GlobalWorkStackQueue globalQueue;
343         WorkStack stack(std::move(workStack));
344         ProcessMarkStack(0, nullptr, stack, globalQueue);
345     }
346 }
347 
AddConcurrentTracingWork(WorkStack & workStack,GlobalWorkStackQueue & globalQueue,size_t threadCount)348 bool MarkingCollector::AddConcurrentTracingWork(WorkStack& workStack, GlobalWorkStackQueue &globalQueue,
349                                                 size_t threadCount)
350 {
351     if (workStack.size() <= threadCount * MIN_MARKING_WORK_SIZE) {
352         return false; // too less init tasks, which may lead to workload imbalance, add work rejected
353     }
354     DCHECK_CC(threadCount > 0);
355     const size_t chunkSize = std::min(workStack.size() / threadCount + 1, MIN_MARKING_WORK_SIZE);
356     // Split the current work stack into work tasks.
357     while (!workStack.empty()) {
358         WorkStackBuf *hSplit = workStack.split(chunkSize);
359         globalQueue.AddWorkStack(WorkStack(hSplit));
360     }
361     return true;
362 }
363 
AddWeakStackClearWork(WeakStack & weakStack,GlobalWeakStackQueue & globalQueue,size_t threadCount)364 bool MarkingCollector::AddWeakStackClearWork(WeakStack &weakStack,
365                                              GlobalWeakStackQueue &globalQueue,
366                                              size_t threadCount)
367 {
368     if (weakStack.size() <= threadCount * MIN_MARKING_WORK_SIZE) {
369         return false; // too less init tasks, which may lead to workload imbalance, add work rejected
370     }
371     DCHECK_CC(threadCount > 0);
372     const size_t chunkSize = std::min(weakStack.size() / threadCount + 1, MIN_MARKING_WORK_SIZE);
373     // Split the current work stack into work tasks.
374     while (!weakStack.empty()) {
375         WeakStackBuf *hSplit = weakStack.split(chunkSize);
376         globalQueue.AddWorkStack(WeakStack(hSplit));
377     }
378     return true;
379 }
380 
PushRootToWorkStack(RootSet * workStack,BaseObject * obj)381 bool MarkingCollector::PushRootToWorkStack(RootSet *workStack, BaseObject *obj)
382 {
383     RegionDesc *regionInfo = RegionDesc::GetAliveRegionDescAt(reinterpret_cast<HeapAddress>(obj));
384     if (gcReason_ == GCReason::GC_REASON_YOUNG && !regionInfo->IsInYoungSpace()) {
385         DLOG(ENUM, "enum: skip old object %p<%p>(%zu)", obj, obj->GetTypeInfo(), obj->GetSize());
386         return false;
387     }
388 
389     // inline MarkObject
390     bool marked = regionInfo->MarkObject(obj);
391     if (!marked) {
392         ASSERT(!regionInfo->IsGarbageRegion());
393         DLOG(TRACE, "mark obj %p<%p>(%zu) in region %p(%u)@%#zx, live %u", obj, obj->GetTypeInfo(), obj->GetSize(),
394              regionInfo, regionInfo->GetRegionType(), regionInfo->GetRegionStart(), regionInfo->GetLiveByteCount());
395         workStack->push_back(obj);
396         return true;
397     } else {
398         return false;
399     }
400 }
401 
PushRootsToWorkStack(RootSet * workStack,const CArrayList<BaseObject * > & collectedRoots)402 void MarkingCollector::PushRootsToWorkStack(RootSet *workStack, const CArrayList<BaseObject *> &collectedRoots)
403 {
404     OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL,
405                  ("CMCGC::PushRootsToWorkStack_" + std::to_string(collectedRoots.size())).c_str(), "");
406     for (BaseObject *obj : collectedRoots) {
407         PushRootToWorkStack(workStack, obj);
408     }
409 }
410 
MarkingRoots(const CArrayList<BaseObject * > & collectedRoots)411 void MarkingCollector::MarkingRoots(const CArrayList<BaseObject *> &collectedRoots)
412 {
413     OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "CMCGC::MarkingRoots", "");
414 
415     WorkStack workStack = NewWorkStack();
416     PushRootsToWorkStack(&workStack, collectedRoots);
417 
418     if (Heap::GetHeap().GetGCReason() == GC_REASON_YOUNG) {
419         OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "CMCGC::PushRootInRSet", "");
420         auto func = [this, &workStack](BaseObject *object) { MarkRememberSetImpl(object, workStack); };
421         RegionSpace &space = reinterpret_cast<RegionSpace &>(Heap::GetHeap().GetAllocator());
422         space.MarkRememberSet(func);
423     }
424 
425     COMMON_PHASE_TIMER("MarkingRoots");
426     VLOG(DEBUG, "roots size: %zu", workStack.size());
427 
428     ASSERT_LOGF(GetThreadPool() != nullptr, "null thread pool");
429 
430     // use fewer threads and lower priority for concurrent mark.
431     const uint32_t maxWorkers = GetGCThreadCount(true) - 1;
432     VLOG(DEBUG, "Concurrent mark with %u threads, workStack: %zu", (maxWorkers + 1), workStack.size());
433 
434     {
435         COMMON_PHASE_TIMER("Concurrent marking");
436         TracingImpl(workStack, maxWorkers > 0, false);
437     }
438 }
439 
Remark()440 void MarkingCollector::Remark()
441 {
442     WorkStack workStack = NewWorkStack();
443     const uint32_t maxWorkers = GetGCThreadCount(true) - 1;
444     OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "CMCGC::Remark[STW]", "");
445     COMMON_PHASE_TIMER("STW re-marking");
446     RemarkAndPreforwardStaticRoots(workStack);
447     ConcurrentRemark(workStack, maxWorkers > 0); // Mark enqueue
448     TracingImpl(workStack, maxWorkers > 0, true);
449     MarkAwaitingJitFort(); // Mark awaiting
450     ClearWeakStack(maxWorkers > 0);
451 
452     OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "CMCGC::MarkingRoots END",
453         ("mark obejects:" + std::to_string(markedObjectCount_.load(std::memory_order_relaxed))).c_str());
454     VLOG(DEBUG, "mark %zu objects", markedObjectCount_.load(std::memory_order_relaxed));
455 }
456 
ClearWeakStack(bool parallel)457 void MarkingCollector::ClearWeakStack(bool parallel)
458 {
459     OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "CMCGC::ProcessGlobalWeakStack", "");
460     {
461         if (gcReason_ == GC_REASON_YOUNG || globalWeakStack_.empty()) {
462             return;
463         }
464         Taskpool *threadPool = GetThreadPool();
465         ASSERT_LOGF(threadPool != nullptr, "thread pool is null");
466         if (parallel) {
467             uint32_t parallelCount = GetGCThreadCount(true);
468             uint32_t threadCount = parallelCount + 1;
469             TaskPackMonitor monitor(parallelCount, parallelCount);
470             GlobalWeakStackQueue globalQueue;
471             for (uint32_t i = 0; i < parallelCount; ++i) {
472                 threadPool->PostTask(std::make_unique<ClearWeakStackTask>(0, *this, threadPool, monitor, globalQueue));
473             }
474             if (!AddWeakStackClearWork(globalWeakStack_, globalQueue, static_cast<size_t>(threadCount))) {
475                 ProcessWeakStack(globalWeakStack_);
476             }
477             bool exitLoop = false;
478             while (!exitLoop) {
479                 WeakStack stack = globalQueue.DrainAllWorkStack();
480                 if (stack.empty()) {
481                     exitLoop = true;
482                 }
483                 ProcessWeakStack(stack);
484             }
485             globalQueue.NotifyFinish();
486             monitor.WaitAllFinished();
487         } else {
488             ProcessWeakStack(globalWeakStack_);
489         }
490     }
491 }
492 
493 
MarkSatbBuffer(WorkStack & workStack)494 bool MarkingCollector::MarkSatbBuffer(WorkStack& workStack)
495 {
496     OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "CMCGC::MarkSatbBuffer", "");
497     COMMON_PHASE_TIMER("MarkSatbBuffer");
498     auto visitSatbObj = [this, &workStack]() {
499         WorkStack remarkStack;
500         auto func = [&remarkStack](Mutator& mutator) {
501             const SatbBuffer::TreapNode* node = mutator.GetSatbBufferNode();
502             if (node != nullptr) {
503                 const_cast<SatbBuffer::TreapNode*>(node)->GetObjects(remarkStack);
504             }
505         };
506         MutatorManager::Instance().VisitAllMutators(func);
507         SatbBuffer::Instance().GetRetiredObjects(remarkStack);
508 
509         while (!remarkStack.empty()) { // LCOV_EXCL_BR_LINE
510             BaseObject* obj = remarkStack.back();
511             remarkStack.pop_back();
512             if (Heap::IsHeapAddress(obj)) {
513                 if (!this->MarkObject(obj)) {
514                     workStack.push_back(obj);
515                     DLOG(TRACE, "satb buffer add obj %p", obj);
516                 }
517             }
518         }
519     };
520 
521     visitSatbObj();
522     return true;
523 }
524 
MarkRememberSetImpl(BaseObject * object,WorkStack & workStack)525 void MarkingCollector::MarkRememberSetImpl(BaseObject* object, WorkStack& workStack)
526 {
527     object->ForEachRefField([this, &workStack, &object](RefField<>& field) {
528         BaseObject* targetObj = field.GetTargetObject();
529         if (Heap::IsHeapAddress(targetObj)) {
530             RegionDesc* region = RegionDesc::GetAliveRegionDescAt(reinterpret_cast<HeapAddress>(targetObj));
531             if (region->IsInYoungSpace() &&
532                 !region->IsNewObjectSinceMarking(targetObj) &&
533                 !this->MarkObject(targetObj)) {
534                 workStack.push_back(targetObj);
535                 DLOG(TRACE, "remember set marking obj: %p@%p, ref: %p", object, &field, targetObj);
536             }
537         }
538     });
539 }
540 
ConcurrentRemark(WorkStack & remarkStack,bool parallel)541 void MarkingCollector::ConcurrentRemark(WorkStack& remarkStack, bool parallel)
542 {
543     LOGF_CHECK(MarkSatbBuffer(remarkStack)) << "not cleared\n";
544 }
545 
MarkAwaitingJitFort()546 void MarkingCollector::MarkAwaitingJitFort()
547 {
548     reinterpret_cast<RegionSpace&>(theAllocator_).MarkAwaitingJitFort();
549 }
550 
Init(const RuntimeParam & param)551 void MarkingCollector::Init(const RuntimeParam& param) {}
552 
Fini()553 void MarkingCollector::Fini() { Collector::Fini(); }
554 
555 #if defined(GCINFO_DEBUG) && GCINFO_DEBUG
DumpHeap(const CString & tag)556 void MarkingCollector::DumpHeap(const CString& tag)
557 {
558     ASSERT_LOGF(MutatorManager::Instance().WorldStopped(), "Not In STW");
559     DLOG(FRAGMENT, "DumpHeap %s", tag.Str());
560     // dump roots
561     DumpRoots(FRAGMENT);
562     // dump object contents
563     auto dumpVisitor = [](BaseObject* obj) {
564         // support Dump
565         // obj->DumpObject(FRAGMENT)
566     };
567     bool ret = Heap::GetHeap().ForEachObject(dumpVisitor, false);
568     LOGE_IF(UNLIKELY_CC(!ret)) << "theAllocator.ForEachObject() in DumpHeap() return false.";
569 
570     // dump object types
571     DLOG(FRAGMENT, "Print Type information");
572     std::set<TypeInfo*> classinfoSet;
573     auto assembleClassInfoVisitor = [&classinfoSet](BaseObject* obj) {
574         TypeInfo* classInfo = obj->GetTypeInfo();
575         // No need to check the result of insertion, because there are multiple-insertions.
576         (void)classinfoSet.insert(classInfo);
577     };
578     ret = Heap::GetHeap().ForEachObject(assembleClassInfoVisitor, false);
579     LOGE_IF(UNLIKELY_CC(!ret)) << "theAllocator.ForEachObject()#2 in DumpHeap() return false.";
580 
581     for (auto it = classinfoSet.begin(); it != classinfoSet.end(); it++) {
582         TypeInfo* classInfo = *it;
583     }
584     DLOG(FRAGMENT, "Dump Allocator");
585 }
586 
DumpRoots(LogType logType)587 void MarkingCollector::DumpRoots(LogType logType)
588 {
589     LOG_COMMON(FATAL) << "Unresolved fatal";
590     UNREACHABLE_CC();
591 }
592 #endif
593 
PreGarbageCollection(bool isConcurrent)594 void MarkingCollector::PreGarbageCollection(bool isConcurrent)
595 {
596     // SatbBuffer should be initialized before concurrent enumeration.
597     SatbBuffer::Instance().Init();
598     // prepare thread pool.
599 
600     GCStats& gcStats = GetGCStats();
601     gcStats.reason = gcReason_;
602     gcStats.async = !g_gcRequests[gcReason_].IsSyncGC();
603     gcStats.gcType = gcType_;
604     gcStats.isConcurrentMark = isConcurrent;
605     gcStats.collectedBytes = 0;
606     gcStats.smallGarbageSize = 0;
607     gcStats.pinnedGarbageSize = 0;
608     gcStats.gcStartTime = TimeUtil::NanoSeconds();
609     gcStats.totalSTWTime = 0;
610     gcStats.maxSTWTime = 0;
611 #if defined(GCINFO_DEBUG) && GCINFO_DEBUG
612     DumpBeforeGC();
613 #endif
614 }
615 
PostGarbageCollection(uint64_t gcIndex)616 void MarkingCollector::PostGarbageCollection(uint64_t gcIndex)
617 {
618     SatbBuffer::Instance().ReclaimALLPages();
619     // release pages in PagePool
620     PagePool::Instance().Trim();
621     collectorResources_.MarkGCFinish(gcIndex);
622 
623 #if defined(GCINFO_DEBUG) && GCINFO_DEBUG
624     DumpAfterGC();
625 #endif
626 }
627 
UpdateGCStats()628 void MarkingCollector::UpdateGCStats()
629 {
630     RegionSpace& space = reinterpret_cast<RegionSpace&>(theAllocator_);
631     GCStats& gcStats = GetGCStats();
632     gcStats.Dump();
633 
634     size_t oldThreshold = gcStats.heapThreshold;
635     size_t oldTargetFootprint = gcStats.targetFootprint;
636     size_t recentBytes = space.GetRecentAllocatedSize();
637     size_t survivedBytes = space.GetSurvivedSize();
638     size_t bytesAllocated = space.GetAllocatedBytes();
639 
640     size_t targetSize;
641     HeapParam& heapParam = BaseRuntime::GetInstance()->GetHeapParam();
642     GCParam& gcParam = BaseRuntime::GetInstance()->GetGCParam();
643     if (!gcStats.isYoungGC()) {
644         gcStats.shouldRequestYoung = true;
645         size_t delta = bytesAllocated * (1.0 / heapParam.heapUtilization - 1.0);
646         size_t growBytes = std::min(delta, gcParam.maxGrowBytes);
647         growBytes = std::max(growBytes, gcParam.minGrowBytes);
648         targetSize = bytesAllocated + growBytes * gcParam.multiplier;
649     } else {
650         gcStats.shouldRequestYoung = gcStats.collectionRate * gcParam.ygcRateAdjustment >= g_fullGCMeanRate
651                                 && bytesAllocated <= oldThreshold;
652         size_t adjustMaxGrowBytes = gcParam.maxGrowBytes * gcParam.multiplier;
653         if (bytesAllocated + adjustMaxGrowBytes < oldTargetFootprint) {
654             targetSize = bytesAllocated + adjustMaxGrowBytes;
655         } else {
656             targetSize = std::max(bytesAllocated, oldTargetFootprint);
657         }
658     }
659 
660     gcStats.targetFootprint = targetSize;
661     size_t remainingBytes = recentBytes;
662     remainingBytes = std::min(remainingBytes, gcParam.kMaxConcurrentRemainingBytes);
663     remainingBytes = std::max(remainingBytes, gcParam.kMinConcurrentRemainingBytes);
664     if (UNLIKELY(remainingBytes > gcStats.targetFootprint)) {
665         remainingBytes = std::min(gcParam.kMinConcurrentRemainingBytes, gcStats.targetFootprint);
666     }
667     gcStats.heapThreshold = std::max(gcStats.targetFootprint - remainingBytes, bytesAllocated);
668     gcStats.heapThreshold = std::max(gcStats.heapThreshold, 20 * MB); // 20 MB:set 20 MB as min heapThreshold
669     gcStats.heapThreshold = std::min(gcStats.heapThreshold, gcParam.gcThreshold);
670 
671     UpdateNativeThreshold(gcParam);
672     Heap::GetHeap().RecordAliveSizeAfterLastGC(bytesAllocated);
673     if (!gcStats.isYoungGC()) {
674         Heap::GetHeap().SetRecordHeapObjectSizeBeforeSensitive(bytesAllocated);
675     }
676 
677     if (!gcStats.isYoungGC()) {
678         g_gcRequests[GC_REASON_HEU].SetMinInterval(gcParam.gcInterval);
679     } else {
680         g_gcRequests[GC_REASON_YOUNG].SetMinInterval(gcParam.gcInterval);
681     }
682     gcStats.IncreaseAccumulatedFreeSize(bytesAllocated);
683     std::ostringstream oss;
684     oss << "allocated bytes " << bytesAllocated << " (survive bytes " << survivedBytes
685                      << ", recent-allocated " << recentBytes << "), update target footprint "
686                      << oldTargetFootprint << " -> " << gcStats.targetFootprint
687                      << ", update gc threshold " << oldThreshold << " -> " << gcStats.heapThreshold;
688     VLOG(INFO, oss.str().c_str());
689     OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "CMCGC::UpdateGCStats END", (
690                     "allocated bytes:" + std::to_string(bytesAllocated) +
691                     ";survive bytes:" + std::to_string(survivedBytes) +
692                     ";recent allocated:" + std::to_string(recentBytes) +
693                     ";update target footprint:" + std::to_string(oldTargetFootprint) +
694                     ";new target footprint:" + std::to_string(gcStats.targetFootprint) +
695                     ";old gc threshold:" + std::to_string(oldThreshold) +
696                     ";new gc threshold:" + std::to_string(gcStats.heapThreshold) +
697                     ";native size:" + std::to_string(Heap::GetHeap().GetNotifiedNativeSize()) +
698                     ";new native threshold:" + std::to_string(Heap::GetHeap().GetNativeHeapThreshold())
699                 ).c_str());
700 }
701 
UpdateNativeThreshold(GCParam & gcParam)702 void MarkingCollector::UpdateNativeThreshold(GCParam& gcParam)
703 {
704     size_t nativeHeapSize = Heap::GetHeap().GetNotifiedNativeSize();
705     size_t newNativeHeapThreshold = Heap::GetHeap().GetNotifiedNativeSize();
706     if (nativeHeapSize < MAX_NATIVE_SIZE_INC) {
707         newNativeHeapThreshold = std::max(nativeHeapSize + gcParam.minGrowBytes,
708                                           nativeHeapSize * NATIVE_MULTIPLIER);
709     } else {
710         newNativeHeapThreshold += MAX_NATIVE_STEP;
711     }
712     newNativeHeapThreshold = std::min(newNativeHeapThreshold, MAX_GLOBAL_NATIVE_LIMIT);
713     Heap::GetHeap().SetNativeHeapThreshold(newNativeHeapThreshold);
714     collectorResources_.SetIsNativeGCInvoked(false);
715 }
716 
CopyObject(const BaseObject & fromObj,BaseObject & toObj,size_t size) const717 void MarkingCollector::CopyObject(const BaseObject& fromObj, BaseObject& toObj, size_t size) const
718 {
719     uintptr_t from = reinterpret_cast<uintptr_t>(&fromObj);
720     uintptr_t to = reinterpret_cast<uintptr_t>(&toObj);
721     LOGE_IF(memmove_s(reinterpret_cast<void*>(to), size, reinterpret_cast<void*>(from), size) != EOK) <<
722         "memmove_s fail";
723 #if defined(COMMON_TSAN_SUPPORT)
724     Sanitizer::TsanFixShadow(reinterpret_cast<void *>(from), reinterpret_cast<void *>(to), size);
725 #endif
726 }
727 
ReclaimGarbageMemory(GCReason reason)728 void MarkingCollector::ReclaimGarbageMemory(GCReason reason)
729 {
730     if (reason != GC_REASON_YOUNG) {
731         Heap::GetHeap().GetAllocator().ReclaimGarbageMemory(true);
732     } else {
733         Heap::GetHeap().GetAllocator().ReclaimGarbageMemory(false);
734     }
735 }
736 
RunGarbageCollection(uint64_t gcIndex,GCReason reason,GCType gcType)737 void MarkingCollector::RunGarbageCollection(uint64_t gcIndex, GCReason reason, GCType gcType)
738 {
739     gcReason_ = reason;
740     gcType_ = gcType;
741     auto gcReasonName = std::string(g_gcRequests[gcReason_].name);
742     auto currentAllocatedSize = Heap::GetHeap().GetAllocatedSize();
743     auto currentThreshold = Heap::GetHeap().GetCollector().GetGCStats().GetThreshold();
744     VLOG(INFO, "Begin GC log. GCReason: %s, GCType: %s, Current allocated %s, Current threshold %s, gcIndex=%llu",
745         gcReasonName.c_str(), GCTypeToString(gcType), Pretty(currentAllocatedSize).c_str(),
746         Pretty(currentThreshold).c_str(), gcIndex);
747     OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "CMCGC::RunGarbageCollection", (
748                     "GCReason:" + gcReasonName + ";GCType:" + GCTypeToString(gcType) +
749                     ";Sensitive:" + std::to_string(static_cast<int>(Heap::GetHeap().GetSensitiveStatus())) +
750                     ";Startup:" + std::to_string(static_cast<int>(Heap::GetHeap().GetStartupStatus())) +
751                     ";Current Allocated:" + Pretty(currentAllocatedSize) +
752                     ";Current Threshold:" + Pretty(currentThreshold) +
753                     ";Current Native:" + Pretty(Heap::GetHeap().GetNotifiedNativeSize()) +
754                     ";NativeThreshold:" + Pretty(Heap::GetHeap().GetNativeHeapThreshold())
755                 ).c_str());
756     // prevent other threads stop-the-world during GC.
757     // this may be removed in the future.
758     ScopedSTWLock stwLock;
759     PreGarbageCollection(true);
760     Heap::GetHeap().SetGCReason(reason);
761     GCStats& gcStats = GetGCStats();
762 
763     DoGarbageCollection();
764 
765     HeapBitmapManager::GetHeapBitmapManager().ClearHeapBitmap();
766 
767     ReclaimGarbageMemory(reason);
768 
769     PostGarbageCollection(gcIndex);
770     MutatorManager::Instance().DestroyExpiredMutators();
771     gcStats.gcEndTime = TimeUtil::NanoSeconds();
772 
773     uint64_t gcTimeNs = gcStats.gcEndTime - gcStats.gcStartTime;
774     double rate = (static_cast<double>(gcStats.collectedBytes) / gcTimeNs) * (static_cast<double>(NS_PER_S) / MB);
775     {
776         std::ostringstream oss;
777         const int prec = 3;
778         oss << "total gc time: " << Pretty(gcTimeNs / NS_PER_US) << " us, collection rate ";
779         oss << std::setprecision(prec) << rate << " MB/s";
780         VLOG(INFO, oss.str().c_str());
781     }
782 
783     g_gcCount++;
784     g_gcTotalTimeUs += (gcTimeNs / NS_PER_US);
785     g_gcCollectedTotalBytes += gcStats.collectedBytes;
786     gcStats.collectionRate = rate;
787 
788     if (!gcStats.isYoungGC()) {
789         if (g_fullGCCount == 0) {
790             g_fullGCMeanRate = rate;
791         } else {
792             g_fullGCMeanRate = (g_fullGCMeanRate * g_fullGCCount + rate) / (g_fullGCCount + 1);
793         }
794         g_fullGCCount++;
795     }
796 
797     UpdateGCStats();
798 
799     if (Heap::GetHeap().GetForceThrowOOM()) {
800         Heap::throwOOM();
801     }
802 }
803 
CopyFromSpace()804 void MarkingCollector::CopyFromSpace()
805 {
806     OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "CMCGC::CopyFromSpace", "");
807     TransitionToGCPhase(GCPhase::GC_PHASE_COPY, true);
808     RegionSpace& space = reinterpret_cast<RegionSpace&>(theAllocator_);
809     GCStats& stats = GetGCStats();
810     stats.liveBytesBeforeGC = space.GetAllocatedBytes();
811     stats.fromSpaceSize = space.FromSpaceSize();
812     space.CopyFromSpace(GetThreadPool());
813 
814     stats.smallGarbageSize = space.FromRegionSize() - space.ToSpaceSize();
815 }
816 
ExemptFromSpace()817 void MarkingCollector::ExemptFromSpace()
818 {
819     RegionSpace& space = reinterpret_cast<RegionSpace&>(theAllocator_);
820     space.ExemptFromSpace();
821 }
822 
823 } // namespace common
824