v8 CodeCache test-api.cc 23440源代码缓存
    优化:增量式 marking(incremental marking)
    优化:分配合并(Allocation Folding)

    三色 marking
    V8 为每一个内存页维护了一个 marking bitmap,页内的每一个可用于分配的字在其中都有一个对应的 bit,由于 V8 中的对象起码是 2 个字的长,所以对象们起码能对应 2 个 bit,于是这个标记最多能有 4 种类型。V8 使用的是一种三色 marking(tricolor marking)的算法,白色代表这个对象可以被回收;黑色代表这个对象不能回收,而且它产生的所有引用都已经扫描完毕;灰色代表这个对象不能被回收,但它产生的引用还没有被扫描完。
    当老生代 GC 启动的时候,V8 会扫描老生代的对象,沿着引用做标记(mark),将这些标记保留在对应的 marking bitmap 里。最开始的时候所有的非根对象带有的标记都是白的,接着 V8 将根对象直接引用的对象放进一个显式的栈,并标记它们为灰色。接下来,V8 从这些对象开始做深度优先搜索,每访问一个对象,就将它 pop 出来,标记为黑色,然后将它引用的所有白色对象标记为灰色,push 到栈上,如此循环往复,直到栈上的所有对象都 pop 掉了为止。这样,最后老生代的对象就只有黑色(不可回收)和白色(可以回收)两种了。
    需要注意的是,当对象太大无法 push 进空间有限的栈的时候,V8 会先把这个对象保留灰色放弃掉,然后将整个栈标记为溢出状态(overflowed)。在溢出状态下,V8 会继续从栈上 pop 对象,标记为黑色,再将引用的白色对象标记为灰色和溢出,但不会将这些灰色的对象 push 到栈上去。这样没多久,栈上的所有对象都被标黑清空了。此时 V8 开始遍历整个堆,把那些同时标记为灰色和溢出对象按照老方法标记完。由于溢出后需要额外扫描一遍堆(如果发生多次溢出还可能扫描多遍),当程序创建了太多大对象的时候,就会显著影响 GC 的效率。
    标记完死亡对象(白色)之后,V8 就可以回收这些死亡对象占用的内存了。回收的方法有两种:sweeping 或者 compacting。
    v8隔离实例内存结构.png