基于 Dart SDK 源码 · heap.cc / scavenger.cc / marker.cc / pages.cc
2026-03-04 | 每日技术深度解读
核心组件:
class Heap {
Scavenger new_space_; // 新生代 (复制收集)
PageSpace old_space_; // 老年代 (标记-清除)
enum Space { kNew, kOld, kCode };
};
Dart VM 的应用:
enum class GCType {
kScavenge, // 新生代复制收集
kEvacuate, // 紧急疏散
kStartConcurrentMark, // 启动并发标记
kMarkSweep, // 标记-清除
kMarkCompact, // 标记-压缩
};
| 类型 | 目标空间 | 特点 |
|---|---|---|
| Scavenge | New | 复制存活对象,速度快 |
| MarkSweep | Old | 标记清除,并发执行 |
| MarkCompact | Old | 压缩碎片,完整GC |
void Scavenger::Scavenge(Thread* thread,
GCType type,
GCReason reason) {
// 1. 翻转半空间
SemiSpace* from = Prologue(reason);
// 2. 复制存活对象
ScavengeRoots();
ScavengeSurvivors();
// 3. 处理弱引用
ProcessWeak();
// 4. 清理 from-space
Epilogue(from);
}
ObjectPtr ScavengeObject(ObjectPtr obj) {
uword header = ReadHeaderRelaxed(obj);
if (IsForwarding(header)) {
// 已转发,返回新地址
return ForwardedObj(header);
}
// 分配新空间
uword new_addr = TryAllocateCopy(size);
// 复制对象
objcpy(new_addr, raw_addr, size);
// 安装转发指针
InstallForwardingPointer(addr, &header,
ForwardingHeader(new_obj));
return new_obj;
}
enum {
kForwardingMask = UntaggedObject::CardRememberedBit::mask(),
kNotForwarded = 0,
kForwarded = kForwardingMask,
};
static bool IsForwarding(uword header) {
return (header & kForwardingMask) == kForwarded;
}
static ObjectPtr ForwardedObj(uword header) {
return static_cast<ObjectPtr>(header);
}
ObjectPtr ScavengeObject(ObjectPtr obj) {
// 检查是否应该晋升
if (!Page::Of(obj)->IsSurvivor(raw_addr)) {
// 新对象,复制到 to-space
new_addr = TryAllocateCopy(size);
}
if (new_addr == 0) {
// Survivor 或空间不足,尝试晋升
new_addr = page_space_->TryAllocatePromoLocked(freelist, size);
if (new_addr == 0) {
// 晋升失败,强制复制到 to-space
new_addr = TryAllocateCopy(size);
}
}
}
DEFINE_FLAG(int, early_tenuring_threshold, 66,
"When more than this percentage of promotion "
"candidates survive, promote all survivors.");
void Epilogue(SemiSpace* from) {
double avg_frac = stats_history_.Get(0)
.PromoCandidatesSuccessFraction();
if (stats_history_.Size() >= 2) {
avg_frac += 0.5 * stats_history_.Get(1)
.PromoCandidatesSuccessFraction();
avg_frac /= 1.5;
}
early_tenure_ = avg_frac >=
(FLAG_early_tenuring_threshold / 100.0);
}
void PageSpace::CollectGarbage(Thread* thread,
bool compact,
bool finalize) {
// 1. 标记阶段
marker_->MarkObjects(this);
// 2. 清除阶段
SweepLarge();
Sweep(/*exclusive*/ true);
// 3. 可选压缩
if (compact) {
Compact(thread);
}
}
void GCMarker::MarkObjects(PageSpace* page_space) {
Prologue();
// 并行标记任务
for (intptr_t i = 0; i < FLAG_marker_tasks; ++i) {
tasks.Append(new ParallelMarkTask(
this, isolate_group_,
&old_marking_stack_,
barrier, visitor, &num_busy));
}
// 执行任务
safepoint_handler->RunTasks(&tasks);
// 弱引用处理
IterateWeakRoots(thread);
Epilogue();
}
颜色定义:
不变式:
bool MarkObject(ObjectPtr obj) {
if (obj->IsImmediateObject()) return false;
// 尝试获取标记位
if (obj->untag()->TryAcquireMarkBit()) {
old_work_list_.Push(obj); // 加入工作列表
}
return false;
}
void GCMarker::StartConcurrentMark(PageSpace* page_space) {
isolate_group_->EnableIncrementalBarrier(
&old_marking_stack_,
&new_marking_stack_,
&deferred_marking_stack_);
// 启动多个并发标记任务
for (intptr_t i = 0; i < num_tasks; i++) {
Dart::thread_pool()->Run<ConcurrentMarkTask>(
this, isolate_group_, page_space, visitor);
}
}
void IncrementalMarkWithSizeBudget(intptr_t size) {
const intptr_t kMinimumMarkingStep = KB;
if (size < kMinimumMarkingStep) return;
MarkingVisitor visitor(...);
visitor.ProcessOldMarkingStack(size);
marked_bytes_ += visitor.marked_bytes();
marked_micros_ += visitor.marked_micros();
}
void IncrementalMarkWithTimeBudget(int64_t deadline) {
visitor.ProcessOldMarkingStackUntil(deadline);
}
// 当写入指针时触发
void VisitPointers(ObjectPtr* first, ObjectPtr* last) {
for (ObjectPtr* current = first; current <= last; current++) {
ObjectPtr obj = LoadPointerIgnoreRace(current);
if (MarkObject(obj)) {
has_evacuation_candidate_ = true;
}
}
// 更新 Store Buffer
if (has_evacuation_candidate_) {
if (obj->untag()->TryAcquireRememberedBit()) {
thread->StoreBufferAddObjectGC(obj);
}
}
}
void IterateStoreBuffers(ScavengerVisitor* visitor) {
StoreBuffer* store_buffer =
heap_->isolate_group()->store_buffer();
for (;;) {
StoreBufferBlock* pending = blocks_;
if (pending == nullptr) break;
while (!pending->IsEmpty()) {
ObjectPtr obj = pending->Pop();
obj->untag()->ClearRememberedBit();
visitor->VisitingOldObject(obj);
visitor->ProcessObject(obj);
}
}
}
intptr_t VisitCards(ArrayPtr obj) {
Page* page = Page::Of(obj);
for (intptr_t i = 0, n = page->card_table_size();
i < n; i++) {
CompressedObjectPtr* card_from = ...;
CompressedObjectPtr* card_to = ...;
VisitCompressedPointers(heap_base, card_from, card_to);
if (has_evacuation_candidate_) {
page->RememberCard(card_from);
}
}
}
void PageSpace::Sweep(bool exclusive) {
GCSweeper sweeper;
while (sweep_regular_ != nullptr) {
Page* page = sweep_regular_;
sweep_regular_ = page->next();
// 清扫页面,回收未标记对象
bool page_in_use = sweeper.SweepPage(page, freelist);
if (!page_in_use) {
// 页面为空,释放
page->Deallocate();
} else {
// 页面有存活对象,加入页面列表
AddPageLocked(page);
}
}
}
void PageSpace::ConcurrentSweep(IsolateGroup* isolate_group) {
GCSweeper::SweepConcurrent(isolate_group);
}
// 页面状态
enum Phase {
kDone, // 空闲
kMarking, // 标记中
kAwaitingFinalization, // 等待最终化
kSweepingLarge, // 清扫大页
kSweepingRegular, // 清扫常规页
};
void PageSpace::Compact(Thread* thread) {
GCCompactor compactor(thread, heap_);
compactor.Compact(pages_, &freelists_[kDataFreelist],
&pages_lock_);
if (FLAG_verify_after_gc) {
heap_->VerifyGC("Verifying after compacting",
kForbidMarked);
}
}
bool ShouldPerformIdleMarkCompact(int64_t deadline) {
const intptr_t excess = capacity - used - 2 * Page::kPageSize;
const double excess_ratio = excess / capacity;
return excess_ratio > 0.05; // 碎片率 > 5%
}
class FreeList {
// 小对象:固定大小桶
FreeListElement* free_lists_[kNumLists];
// 大对象:独立链表
FreeListElement* free_list_;
uword TryAllocate(intptr_t size) {
if (size <= kMaxSmallAlloc) {
// 从对应桶分配
int index = IndexForSize(size);
return TryAllocateSmall(index, size);
} else {
// 从大对象链表查找
return TryAllocateLarge(size);
}
}
};
class PageSpace {
Page* pages_; // 常规数据页
Page* exec_pages_; // 可执行代码页
Page* large_pages_; // 大对象页
Page* image_pages_; // 镜像页
Page* AllocatePage(bool is_exec) {
Page* page = Page::Allocate(Page::kPageSize, flags);
if (is_exec) {
AddExecPageLocked(page);
} else {
AddPageLocked(page);
}
return page;
}
};
class Thread {
uword top_; // 当前分配指针
uword end_; // 缓冲区结束位置
};
uword TryAllocateCopy(intptr_t size) {
if (tail_ != nullptr) {
uword result = tail_->top_;
uword new_top = result + size;
if (new_top <= tail_->end_) {
tail_->top_ = new_top;
return result; // 快速路径,无锁
}
}
return TryAllocateCopySlow(size);
}
class GcSafepointOperationScope {
GcSafepointOperationScope(Thread* thread) {
isolate_group()->safepoint_handler()
->SafepointThreads(thread, SafepointLevel::kGC);
}
~GcSafepointOperationScope() {
isolate_group()->safepoint_handler()
->ResumeThreads(thread, SafepointLevel::kGC);
}
};
void CollectGarbage(Thread* thread, ...) {
GcSafepointOperationScope safepoint(thread);
// 执行 GC...
}
enum class GCReason {
kNewSpace, // 新生代空间不足
kStoreBuffer, // Store Buffer 溢出
kPromotion, // 晋升触发
kOldSpace, // 老年代空间不足
kFinalize, // 最终化标记
kFull, // 完整 GC
kExternal, // 外部内存分配
kIdle, // 空闲时 GC
kDestroyed, // Isolate 销毁
kDebugging, // 调试触发
kCatchUp, // 追赶模式
};
void Heap::NotifyIdle(int64_t deadline) {
GcSafepointOperationScope safepoint(thread);
// 空闲 Scavenge
if (new_space_.ShouldPerformIdleScavenge(deadline)) {
CollectNewSpaceGarbage(thread, GCType::kScavenge,
GCReason::kIdle);
}
// 空闲 Mark-Compact
if (old_space_.ShouldPerformIdleMarkCompact(deadline)) {
CollectOldSpaceGarbage(thread, GCType::kMarkCompact,
GCReason::kIdle);
}
}
intptr_t ProcessFinalizerEntry(FinalizerEntryPtr entry) {
delayed_.finalizer_entries.Enqueue(entry);
// 只访问 token 和 next
MarkObject(entry->untag()->token_);
MarkObject(entry->untag()->next_);
return entry->untag()->HeapSize();
}
void MournFinalizerEntries() {
FinalizerEntryPtr current = delayed_.finalizer_entries.Release();
while (current != FinalizerEntry::null()) {
MournFinalizerEntry(this, current);
current = current->untag()->next_seen_by_gc();
}
}
intptr_t ProcessWeakProperty(WeakPropertyPtr weak) {
ObjectPtr key = weak->untag()->key();
if (key->IsHeapObject() && !key->untag()->IsMarked()) {
// 键未标记,入队延迟处理
delayed_.weak_properties.Enqueue(weak);
return weak->untag()->HeapSize();
}
// 键已标记,正常访问
return weak->untag()->VisitPointersNonlocal(this);
}
void MournWeakProperties() {
// 清除键值对
weak->untag()->key_ = Object::null();
weak->untag()->value_ = Object::null();
}
intptr_t ProcessWeakReference(WeakReferencePtr weak) {
ObjectPtr target = weak->untag()->target();
if (target->IsHeapObject() && !target->untag()->IsMarked()) {
// 目标未标记,入队延迟处理
delayed_.weak_references.Enqueue(weak);
}
// 始终访问类型参数
MarkObject(weak->untag()->type_arguments_);
return weak->untag()->HeapSize();
}
void MournWeakReferences() {
// 如果目标被回收,置空
ForwardOrSetNullIfCollected(weak, &weak->untag()->target_);
}
intptr_t ProcessWeakArray(WeakArrayPtr weak) {
// 所有 WeakArray 入队延迟处理
delayed_.weak_arrays.Enqueue(weak);
return weak->untag()->HeapSize();
}
void MournWeakArrays() {
WeakArrayPtr current = delayed_.weak_arrays.Release();
while (current != WeakArray::null()) {
intptr_t length = Smi::Value(current->untag()->length());
for (intptr_t i = 0; i < length; i++) {
// 被回收的元素置空
ForwardOrSetNullIfCollected(
current, ¤t->untag()->data()[i]);
}
current = current->untag()->next_seen_by_gc();
}
}
struct SpaceUsage {
intptr_t capacity_in_words;
intptr_t used_in_words;
intptr_t external_in_words;
};
class GCStats {
intptr_t num_; // GC 次数
GCType type_; // GC 类型
GCReason reason_; // GC 原因
Data before_; // GC 前状态
Data after_; // GC 后状态
};
void PrintStats() {
OS::PrintErr(
"[ GC isolate | space (reason) | GC# | time | "
"new gen used/cap/ext | old gen used/cap/ext ]\n"
"[ %-13s, %11s(%12s), %4" Pd ", %5.1fms, "
"%5.1f/%5.1f/%3.1f MB, %6.1f/%6.1f/%5.1f MB ]\n",
isolate_name, GCTypeToString(type_),
GCReasonToString(reason_),
num_, duration_ms,
new_before, new_capacity, new_external,
old_before, old_capacity, old_external);
}
bool Heap::VerifyGC(const char* msg,
MarkExpectation expect) {
// 创建已分配对象集合
ObjectSet* allocated = CreateAllocatedObjectSet(zone, expect);
// 验证所有指针指向有效对象
VerifyPointersVisitor visitor(isolate_group_, allocated, msg);
VisitObjectPointers(&visitor);
return true;
}
// 验证选项
DEFINE_FLAG(bool, verify_before_gc, false,
"Verify heap before GC");
DEFINE_FLAG(bool, verify_after_gc, false,
"Verify heap after GC");
uword Heap::AllocateOld(Thread* thread, intptr_t size,
bool is_exec) {
// 尝试各种 GC 策略
CollectOldSpaceGarbage(thread, GCType::kMarkSweep, ...);
// 最后尝试强制增长
addr = old_space_.TryAllocate(size, is_exec, kForceGrowth);
if (addr == 0) {
// 释放 OOM 保留空间
old_space_.TryReleaseReservation();
OS::PrintErr("Exhausted heap space, size=%" Pd "\n", size);
isolate_group_->set_has_seen_oom(true);
}
return addr;
}
新生代调优:
老年代调优:
| 参数 | 默认值 | 说明 |
|---|---|---|
| new_gen_semi_initial_size | - | 新生代初始大小 |
| new_gen_garbage_threshold | 90 | 垃圾占比阈值 |
| new_gen_growth_factor | 2 | 新生代增长因子 |
| early_tenuring_threshold | 66 | 早期晋升阈值 |
| old_gen_growth_space_ratio | 20 | 老年代增长空间比 |
| old_gen_growth_time_ratio | 3 | 老年代增长时间比 |
| concurrent_mark | true | 启用并发标记 |
| marker_tasks | 2 | 标记任务数 |
void RunParallelGC() {
const intptr_t num_tasks =
heap_->new_space()->NumScavengeWorkers();
ThreadBarrier* barrier =
new ThreadBarrier(num_tasks, /*initial=*/1);
IntrusiveDList<SafepointTask> tasks;
for (intptr_t i = 0; i < num_tasks; i++) {
tasks.Append(new ParallelMarkTask(...));
}
safepoint_handler->RunTasks(&tasks);
}
class ParallelMarkTask : public SafepointTask {
void RunEnteredIsolateGroup() override {
// 阶段 1: 标记根和标记栈
num_busy_->fetch_add(1u);
marker_->IterateRoots(visitor_);
do {
visitor_->DrainMarkingStack();
} while (visitor_->WaitForWork(num_busy_));
// 阶段 2: 延迟标记
visitor_->ProcessDeferredMarking();
// 阶段 3: 弱引用处理
visitor_->MournWeakProperties();
visitor_->MournWeakReferences();
marker_->IterateWeakRoots(thread);
}
};
class ParallelSweepTask : public SafepointTask {
void RunEnteredIsolateGroup() override {
// 清扫可执行页面
old_space_->SweepExecutable();
// 清扫新生代页面
if (!new_space_is_swept_) {
old_space_->SweepNew();
}
}
};
void Sweep(bool exclusive) {
GCSweeper sweeper;
// 轮询分片,均匀分配到各个空闲链表
shard = (shard + 1) % num_shards;
FreeList* freelist = DataFreeList(shard);
sweeper.SweepPage(page, freelist);
}
DEFINE_FLAG(charp, heap_snapshot_on_oom, nullptr,
"Write a heap snapshot after OOM");
void OnOOM() {
#if defined(DART_ENABLE_HEAP_SNAPSHOT_WRITER)
if (FLAG_heap_snapshot_on_oom != nullptr) {
FileHeapSnapshotWriter file_writer(
thread, FLAG_heap_snapshot_on_oom, &successful);
HeapSnapshotWriter writer(thread, &file_writer);
writer.Write();
}
#endif
}
Dart DevTools:
VM Service API:
// 启用堆采样
void SetHeapSamplingData(ObjectPtr obj, void* data) {
SetWeakEntry(obj, kHeapSamplingData,
reinterpret_cast<intptr_t>(data));
}
// 监控指标
void PrintMemoryUsageJSON(JSONObject* jsobj) {
jsobj->AddProperty("type", "MemoryUsage");
jsobj->AddProperty64("heapUsage",
TotalUsedInWords() * kWordSize);
jsobj->AddProperty64("heapCapacity",
TotalCapacityInWords() * kWordSize);
jsobj->AddProperty64("externalUsage",
TotalExternalInWords() * kWordSize);
}
// RSS 监控
static void RecordRSS() {
TimelineEvent* event = Timeline::GetGCStream()->StartEvent();
event->FormatArgument(0, "value", "%" Pd, OS::CurrentRSS());
}
https://github.com/dart-lang/sdk