程序员

v8的垃圾回收机制

2023-01-30  本文已影响0人  super_wei

        由于js是单线程,垃圾回收会占用主线程,会照成页面的卡顿。所以我们需要为垃圾回收提供一种算法或者策略来解决这个问题。V8采用的是分代式回收,按照对象的存活时间将内存分为不同的分代,并对不同的分代使用不同的算法来提高垃圾回收机制的效率。
        V8中,主要把内存分为新生代和老生代,新生代是存活时间较短的对象,老生代是存活时间较长的对象。


新生代与Scavenge算法

    在分代的基础上,新生代中的对象主要通过Scavenge算法进行垃圾回收。其将内存一分为二,分为From区和To区。
    

实际使用的堆内存是新生代中的两个空间大小和老生代所用内存大小之和

在From区用来分配,当垃圾回收机制运行的时候会检查From空间对象,没有存活的对象会被释放,存活的对象会被复制到To区。可以理解为Scavenge算法只是运用在From区。等复制动作完成后,From空间和To空间的角色发生对换。

然后还有个动作是新生代晋升成老生代:新生代的对象晋升为老生代的条件有两个:
1、当一个对象经过多次复制依然存在的时候,将会被认为是存活长的对象,会被移到老生代当中,采用新的算法进行管理。

在单纯的Scavenge过程中,From空间中的存活对象会被复制到To空间中去,然后对From空间和To空间进行角色对换(又称翻转)。但在分代式垃圾回收的前提下,From空间中的存活对象在复制到To空间之前需要进行检查。在一定条件下,需要将存活周期长的对象移动到老生代中,也就是完成对象晋升。

2、另一个判断是To空间的内存占比,当From区复制一个对象到To区的时候,如果To区空间使用了超过25%,则这个对象就会直接晋升到老生代的空间中,否则会影响到后续From的分配(复制后To区内容会换到From区)。

老生代和Mark-Sweep(标记清除)、Mark-Compact(标记整理)

    相对于Scavenge是处理还存活的对象,老生代由于存活对象占比太大,复制存货效率会降低,所以用Mark-Sweep和Mark-Compact相结合的方式进行垃圾回收。
    Mark-Sweep标记清除,并不会将空间分为两个部分,所以不存在浪费空间,Mark-Sweep会便利堆中的所有对象,标记存活的对象,后续只清除没有被标记的对象,因为死去的对象在老生代中比较少,所以效率提高。

清楚后内存空间出现断断续续的情况

    Mark-Sweep最大的问题是在进行一次标记清除回收后,内存空间会出现不连续的状态。这种内存碎片会对后续的内存分配造成问题,因为很可能出现需要分配一个大对象的情况,这时所有的碎片空间都无法完成此次分配,就会提前触发垃圾回收,而这次回收是不必要的。
    为了解决这个问题,Mark-Compact被提出来。Mark-Compact是标记整理的意思,是在Mark-Sweep的基础上演变而来的。它们的差别在于对象在标记为死亡后,在整理的过程中,将活着的对象往一端移动,移动完成后,直接清理掉边界外的内存。

完成移动后,就可以直接清除最右边的存活对象后面的内存区域完成回收。

Mark-Sweep和Mark-Compact之间,由于Mark-Compact需要移动对象,所以它的执行速度不可能很快,所以在取舍上,V8主要使用Mark-Sweep,在空间不足以对从新生代中晋升过来的对象进行分配时才使用Mark-Compact。

上一篇下一篇

猜你喜欢

热点阅读