面向C++·V8的高性能垃圾回收

2020-05-27 05:02:38

在过去,我们已经写过关于JavaScript的垃圾收集、文档对象模型(DOM),以及如何在V8中实现和优化所有这些内容。不过,Chromium中并不是所有的东西都是JavaScript,因为嵌入了V8的浏览器及其Blink渲染引擎的大部分都是用C++编写的。JavaScript可用于与DOM交互,然后由呈现管道处理该DOM。因为DOM周围的C++对象图与Javascript对象纠缠在一起,Chromium团队在几年前就转而使用称为OilPan的垃圾收集器来管理这种内存。OilPan是一个用C++编写的垃圾收集器,用于管理C++内存,它可以使用跨组件跟踪连接到V8,跨组件跟踪将纠结的C++/JavaScript对象图视为一个堆。这篇文章是Oilpan一系列博客文章中的第一篇,这些文章将概述Oilpan语言及其C++API的核心原理。在这篇文章中,我们将介绍一些受支持的特性,解释它们如何与垃圾收集器的各个子系统交互,并深入研究在清扫器中并发回收对象。最令人兴奋的是,OilPan目前在Blink中实现,但将以垃圾收集库的形式迁移到V8。我们的目标是让所有V8嵌入者和更多的C++开发人员都可以轻松地使用C++垃圾回收。OilPan实现了标记-清除垃圾收集器,其中垃圾收集分为两个阶段:标记在哪里扫描托管堆中的活动对象,以及在哪里清理托管堆上的死对象被回收。在V8中引入并发标记时,我们已经介绍了标记的基础知识。简单地说,扫描所有对象中的活动对象可以看作是图的遍历,其中对象是节点,对象之间的指针是边。遍历从寄存器、本机执行堆栈(从现在起我们将称之为堆栈)和其他全局变量的根开始,如下所述。在这方面,C++与JavaScript没有什么不同。不过,与JavaScript不同的是,C++对象是静态类型的,因此不能在运行时更改其表示形式。使用OilPan管理的C++对象利用这一事实,并通过访问者模式提供指向其他对象(图中的边)的指针的描述。描述油底壳对象的基本模式如下:class final:public GarbageCollect<;LinkedNode>;{public:LinkedNode(LinkedNode*Next,int value):Next_(Next),value_(Value){}void Trace(Visitor*Visitor)const{Visitor->;Trace(Next_);}Private:Member<;LinkedNode>;Next_;int value_;};LinkedNode*CreateNodes(){LinkedNode*First_Node=MakeGarbageCollect<;LinkedNode&>(nullptr,1);LinkedNode*Second_Node=MakeGarbageCollect<;LinkedNode>;(First_Node,2);return Second_Node;}。

在上面的示例中,LinkedNode由OilPan管理,如从GarbageCollect<;LinkedNode>;继承所示。当垃圾回收器处理对象时,它通过调用该对象的Trace方法来发现传出指针。类型成员是语法上类似于STD::SHARED_PTR的智能指针,后者由Oilspan提供,用于在标记期间遍历图形时保持一致状态。所有这些都使OilPan能够精确地知道指针驻留在其托管对象中的什么位置。狂热的读者可能会注意到并且可能会害怕,在上面的示例中,first_node和Second_node被保留为堆栈上的原始C++指针。Oilspan不添加使用堆栈的抽象,仅依靠保守的堆栈扫描在处理根时查找指向其托管堆的指针。这是通过逐字迭代堆栈并将这些字解释为指向托管堆的指针来实现的。这意味着OilPan不会对访问堆栈分配的对象施加性能损失。相反,它将成本转移到垃圾收集时间,在那里它保守地扫描堆栈。集成在呈现器中的Oilpan会尝试延迟垃圾收集,直到它达到保证没有有趣堆栈的状态。由于Web是基于事件的,并且执行是由事件循环中的处理任务驱动的,因此这样的机会非常多。Blink是一个拥有大量成熟代码的大型C++代码库,因此也支持:通过混合和对此类混合(内部指针)的引用实现多重继承。在执行构造函数期间触发垃圾回收。通过被视为根的持久智能指针使非托管内存中的对象保持活动状态。覆盖顺序(例如,矢量)和关联(例如,集合和映射)容器的集合,具有集合背景的压缩。

请继续关注另一篇博客文章,详细介绍油底锅打标的工作原理。对于这篇文章,我们