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