JavaScript性能优化 - 标记清除算法优缺点

307 阅读3分钟

标记清除算法的优点

相对于引用计数来讲,标记清除具有一个最大的优点。就是它可以去解决之前对象循环引用的回收操作。比如我们在一个函数内定义了两个对象 a1、b1,并让它们互相引用。对于这种函数的调用,在结束之后必然要去释放它们内部的空间。所以在这种情况下,一旦当某一个函数调用结束之后它局部空间当中的变量就失去了与当前全局 global 在作用域上的一个连接。所以 a1、b1 在全局 global 下就无法访问到,这个时候它就是一个不可达的对象。不可达的对象在做标记阶段的时候,就不能够完成标记。那么接下来在第二个阶段,我们当前去回收的时候就直接找到这些没有没标记的对象,把它内部的空间进行了释放。这是标记清除可以做到的事情,但是在引用计数里面,虽然当前这个函数调用结束以后它内部的 a1、b1 也没办法在全局的地方去进行访问,可是由于当前判断的标准是引用数值是否为 0 ,所以在这种情况下它就没有办法去释放 a1、b1的空间。这就是标记清楚算法的最大优点,是相对于引用计数算法来说的。

标记清除算法的缺点

如下图我们模拟了一个内存的存储情况,我们当前从根去进行查找,在下方又一个直接的可达对象就是红色标示部位。在它的左右两侧有两块从根无法查找的区域,这种情况在进行第二轮清除操作的时候它就会直接将两块区域所对应的空间进行回收,也就是蓝色表示的区域。然后再把这样一个释放的空间,添加到空闲列表之中。紧接着后续的程序就可以直接去再从空闲列表上去申请相应的空间地址使用,不过在这种情况下就会有一个问题。任何一个空间都会有两个部分组成,一个是存储这个空间原信息的比如它的大小、地址等等,称为为头;另一个部分是用来专门存储数据的,叫做域。经过标记清除回收操作过后,左右两边就要三个字的可用空间。由于它们中间有个可达对象的使用空间分割着,所以在释放完成过后它们其实还是分散的也就是地址不连续。这点很重要地址不连续所以在这种情况下,如果后续我们想去申请一片空间。而刚好巧了这次我们想申请的空间地址大小刚好是 1.5 个字,这种情况下如果找到 B 所释放的空间,会发现它是多了 0.5 个,找 C 又少 0.5 个。这就造成当前标记算法中最大的问题,叫做空间的碎片化。

所谓的空间碎片化,就是由于当前所回收的垃圾对象在地址上它本身是不连贯的。由于这种不连续从而造成了在回收之后它们分散在各个角落,后续想要去使用的时候如果刚好巧了新的生成空间刚好与它们大小匹配就能直接用,一旦是多了或是少了就不太适合去使用了。所以这就是标记清除算法的一个缺点,我们称之为空间碎片化。

总结:

  • 优点:相对于引用计数来说,可以去解决循环引用不能回收的问题;
  • 缺点:会产生空间碎片化的问题,不能让空间得到最大化的使用;