V8引擎系列(3):memory structure and gc

991 阅读3分钟

本文是系列文章第三篇,详情见第一篇

memory structure

一个运行着的程序在v8被分配的内存空间被称为Resident Set

image.png

其中stack区保存的是静态分配的数据,保存函数frame、primitive变量,和指向对象变量的指针。栈底是global frame保存全局的数据,在程序运行期间一直存在,其他部分是执行上下文栈,最上面的是正在执行的上下文,当执行上下文的代码开始执行时,变量的作用域就会产生。
这个区域的内存管理由OS控制。

heap区保存的是动态分配的数据,内存管理由v8控制,因此也是后面讨论gc发生的地方。
young generation下一节会深入讨论,old generation又分为map space和old object space,map space只包含map 对象,即前文讲的Shape,其余部分包含old object。
large object space保存超过一定尺寸限制的对象,larget object会在large object space创建,并适时释放,不会被移动
code space是编译器保存编译后代码块的地方,可能会和large object space有交集,没在图中表示。

gc

v8中有两个garbage collectors,第一个是Major GC (Mark-Compact),负责整个heap的垃圾回收,另一个是Minor GC (Scavenger),只负责young generation的垃圾回收。

Mark-Compact

gc在这里包括marking, sweeping and compacting三个阶段。

  • Marking gc会从一系列已知的对象指针开始,它们被称为root set,这包括 the execution stack and the global object,然后沿着每个指针递归查询,并将被遍历过的对象标记为reachable,用来表示当前依然需要保留

  • Sweeping gc会将没标记的那些视为dead objects,然后将其添加到一个叫较free-list的数据结构,以便将来用来分配内存。

  • Compaction 将surviving objects复制到当前没compacted的区域,这样就可以把dead objects留下来小而分散的内存利用起来。 但如果存在很多surviving objects,这种复制就会很消耗性能,因此只会对很碎片的片段处理。

image.png

Scavenger

将heap被分为young generation and an old generation,Scavenger发生在young generation,young generation分为两半空间,其中一半总是空的,空的一半被称为To-Space,另一半被称为From-Space,一次gc后,reachable的对象从From-Space复制到To-Space。此时两个空间互换。
根据假说The Generational Hypothesis,即,大部分对象被分配后会立刻变得unreachable,只需要复制少量对象.

image.png 如果一直这样,young generation很快就会变满,因此young generation又可细分为两个子代: 对象首次分配会分配到nursery,如果下一次gc后存在就会转移到intermediate,如果再一次仍然存在就会移动到old generation

image.png image.png

Orinoco

Orinoco是v8垃圾回收器项目的代号,包括以下技术

  1. Parallel
    main线程和helper线程同时处理,会阻塞主线程,也是上面介绍的两种垃圾回收正在使用的方式

image.png

  1. Incremental 增量处理,减少主线程阻塞的时间。 堆的状态变化会导致一些增量处理无效。

image.png

  1. Concurrent 主线程不会阻塞,只用helper线程垃圾回收。这种有前一种的问题,也要处理好主线程和helper线程的读写竞争和helper线程之间的信息同步问题。

Idle-time GC

除了v8自身的gc,还提供给了一种机制给embedder(比如浏览器)触发垃圾回收:v8会发布idle tasks,可以被浏览器等在每一帧空闲时触发。

image.png

这也可以看作是v8提供的的第三种gc。

参考