大家好,我是徐徐。今天我们来从 V8 的源码来分析分析浏览器的垃圾回收机制。
前言
最近在读 Chromium 的源码,然后发现了里面的 V8 目录,这不就是我们天天在说的 V8 引擎吗,于是就看了起来。但是里面的东西的确很复杂,就想到关于 V8 的一些问题,最常见的就是垃圾回收机制的解读,于是我就找到了这一块的源码来看,现在开始进入正题。
在现代 Web 应用中,JavaScript 的性能至关重要。而垃圾回收(Garbage Collection,简称GC)作为内存管理的核心机制,直接影响着JavaScript的执行效率。本文将解析V8引擎的源码,剖析其垃圾回收的实现原理,如果你是直接在 GitHub 直接下载的 Chromium 源码里面是没有 V8 的源码的,需要同步所有代码后才能看到,如果想单独看需要在这里看:
浏览器引擎概述
现代主流浏览器主要使用以下几种JavaScript引擎:
- V8: 由Google开发,用于Chrome和Node.js
- SpiderMonkey: Mozilla开发,用于Firefox
- JavaScriptCore: Apple开发,用于Safari
本文将主要关注V8引擎,因为它不仅应用广泛,而且有丰富的文档和开放的源代码。
垃圾回收基础
在深入V8的实现之前,我们需要了解几种常见的垃圾回收算法:
标记-清除(Mark and Sweep):
- 标记阶段:从根对象开始,标记所有可达对象
- 清除阶段:清除所有未标记对象
复制算法(Copying):
- 将内存分为两个相等的区域
- 将存活对象从一个区域复制到另一个区域
- 清空原有区域
分代收集(Generational Collection):
- 将对象按年龄分类(新生代和老生代)
- 对不同代的对象采用不同的回收算法
V8中的垃圾回收实现
V8采用了分代回收的策略,将堆内存分为新生代和老生代。
新生代(Young Generation):
- 使用半空间(Semi-space)设计
- 采用Scavenge算法进行回收
- 适用于生命周期短的对象
老生代(Old Generation):
- 使用标记-清除和标记-整理算法
- 适用于生命周期长的对象
V8垃圾回收核心代码解析
源码位置:
下面是代码中的几个核心逻辑部分及其简要解析:
ScavengerCollector 类
ScavengerCollector 类负责管理和执行新生代对象的垃圾回收。主要方法包括:
- CollectGarbage:这是垃圾回收的入口函数,执行垃圾回收的主要步骤,包括标记、复制和处理弱引用。
- ProcessWeakReferences:处理弱引用,确保在垃圾回收过程中正确处理弱引用对象。
- ScavengePage:处理内存页中的对象,检查并转移新生代对象。
void ScavengerCollector::CollectGarbage() {
// 初始化和准备阶段
auto* new_space = SemiSpaceNewSpace::From(heap_->new_space());
new_space->GarbageCollectionPrologue();
new_space->EvacuatePrologue();
// 翻转新生代大对象空间
heap_->new_lo_space()->Flip();
heap_->new_lo_space()->ResetPendingObject();
// 创建多个 Scavenger 对象用于并行回收
std::vector<std::unique_ptr<Scavenger>> scavengers;
Scavenger::EmptyChunksList empty_chunks;
const int num_scavenge_tasks = NumberOfScavengeTasks();
Scavenger::CopiedList copied_list;
Scavenger::PromotionList promotion_list;
EphemeronRememberedSet::TableList ephemeron_table_list;
{
// 初始化 Scavenger 对象
for (int i = 0; i < num_scavenge_tasks; ++i) {
scavengers.emplace_back(
new Scavenger(this, heap_, is_logging, &empty_chunks, &copied_list,
&promotion_list, &ephemeron_table_list, i));
}
// 处理根对象
RootScavengeVisitor root_scavenge_visitor(scavengers[kMainThreadId].get());
heap_->IterateRoots(&root_scavenge_visitor, options);
isolate_->global_handles()->IterateYoungStrongAndDependentRoots(
&root_scavenge_visitor);
isolate_->traced_handles()->IterateYoungRoots(&root_scavenge_visitor);
scavengers[kMainThreadId]->Publish();
// 并行处理对象
auto job =
std::make_unique<JobTask>(this, &scavengers, std::move(memory_chunks),
&copied_list, &promotion_list);
V8::GetCurrentPlatform()
->CreateJob(v8::TaskPriority::kUserBlocking, std::move(job))
->Join();
// 处理弱引用
ProcessWeakReferences(&ephemeron_table_list);
}
// 其他清理和更新操作
SemiSpaceNewSpace* semi_space_new_space =
SemiSpaceNewSpace::From(heap_->new_space());
semi_space_new_space->set_age_mark_to_top();
heap_->new_lo_space()->FreeDeadObjects(
[](Tagged<HeapObject>) { return true; });
// 更新全局和追踪句柄
isolate_->global_handles()->UpdateListOfYoungNodes();
isolate_->traced_handles()->UpdateListOfYoungNodes();
}
Scavenger 类
Scavenger 类负责实际的垃圾回收操作,包括对象的标记和复制。主要方法包括:
- Process:处理被标记的对象,进行垃圾回收。
- Finalize:最终阶段,合并和清理回收结果。
- ScavengePage:处理内存页中的对象。
void Scavenger::Process(JobDelegate* delegate) {
ScavengeVisitor scavenge_visitor(this);
bool done;
size_t objects = 0;
do {
done = true;
ObjectAndSize object_and_size;
while (!promotion_list_local_.ShouldEagerlyProcessPromotionList() &&
copied_list_local_.Pop(&object_and_size)) {
scavenge_visitor.Visit(object_and_size.first);
done = false;
if (delegate && ((++objects % kInterruptThreshold) == 0)) {
if (!copied_list_local_.IsLocalEmpty()) {
delegate->NotifyConcurrencyIncrease();
}
}
}
struct PromotionListEntry entry;
while (promotion_list_local_.Pop(&entry)) {
Tagged<HeapObject> target = entry.heap_object;
IterateAndScavengePromotedObject(target, entry.map, entry.size);
done = false;
if (delegate && ((++objects % kInterruptThreshold) == 0)) {
if (!promotion_list_local_.IsGlobalPoolEmpty()) {
delegate->NotifyConcurrencyIncrease();
}
}
}
} while (!done);
}
RootScavengeVisitor 类
RootScavengeVisitor 类负责遍历根对象并标记需要回收的对象。
void RootScavengeVisitor::ScavengePointer(FullObjectSlot p) {
Tagged<Object> object = *p;
if (Heap::InYoungGeneration(object)) {
scavenger_->ScavengeObject(FullHeapObjectSlot(p), Cast<HeapObject>(object));
}
}
通过这些类和方法的协同工作,V8 引擎能够高效地进行新生代对象的垃圾回收,确保内存的及时释放和重用。整个过程可以总结为:
- 准备工作:调用
GarbageCollectionPrologue和EvacuatePrologue方法准备垃圾回收。 - 遍历对象:使用
IterateAndScavengePromotedObjectsVisitor类遍历和处理被提升的对象。 - 并行处理:通过多个
Scavenger实例并行处理对象,提升性能。 - 更新引用:更新全局句柄列表中的引用,确保没有悬挂引用。
- 完成工作:调用
Finalize方法完成垃圾回收,并更新新生代空间的状态。
结语
以上只是我简单的一些分析,其实里面还有很多其他的文件,以及需要注意的地方。比如内存分配策略的实现,标记工作的具体实现,以及内存压缩技术,垃圾回收触发的机制等等,都有待分析。分析归分析,在实际开发中,我们应该注意以下几个点:
- 避免频繁创建短生命周期的对象
- 合理使用内存,及时释放不需要的引用
- 使用性能分析工具识别和解决内存问题
- 理解垃圾回收的工作原理,编写GC友好的代码
浏览器的源码是真的牛,还是多学学吧,现在学C++应该还来得及,卷不动了。