【Lua基础系列】垃圾回收机制
大家好,我是Lampard~~
欢迎来到Lua进阶系列的博客
前文再续,书接上一回。今天和大家讲解一下lua中的垃圾回收机制
在 Lua 中,一共只有8种数据类型,分别为 nil 、boolean 、userdata 、number 、string 、 table 、 function 、 userdata 和 thread 。其中,只有 string table function thread 四种是以引用方式共享,是需要被 GC 管理回收的对象。
Lua采用的是Mark-sweep算法:
mark阶段
这个阶段叫做扫描阶段。简单来讲,就是对于现在lua用到的所有对象进行扫描一次。如果某个对象当前跟别的对象有引用关系,那么说明他还在用;如果某个对象跟其他任何对象都没有引用关系了,说明这个对象已经没有用了。这个阶段做完,就可以知道哪些对象还有用,哪些对象不再使用了,下面就交给下一个阶段,sweep阶段。
cleaning阶段
这个阶段lua会出里对象的析构和弱引用表,它会遍历标记需要析构的对象,以及遍历弱引用表将要移除的键或者值
sweep阶段
这个阶段做的事情其实很少,关键步骤在前一个阶段做完了。这个阶段根据前一个扫描阶段得到的结果,遍历一遍所有对象。如果这个对象已经标记为不再使用了,就会被清理掉,释放掉所在的内存;如果这个对象还在使用,那么就处理一下状态,等待下一次gc在处理。
finalization析构
对标记需要析构的对象进行析构
这里添加一下对弱表的介绍:若一个存放在表中,那么哪怕这个对象没有被任何地方引用,但是也不会被清除,因为此时这个对象就正在被这个表引用。为了解决这个问题可以在表中的__mode字段来定义该表是一个弱表,那么在GC的时候才会把它给回收掉
__mode = "k" -- 代表这个表中的键是弱引用的弱引用表
__mode = “v” -- 代表这个表中的值是弱引用的弱引用表
__mode = “kv” -- 代表这个表中的键值都是弱引用的弱引用表
无论哪一种情况,只要其中一个键或者值被回收了,那么整个键值对就会被回收,这和我们把变量置位nil其实是将它 删除的原理是一样的
缺陷:
在lua5.0之前,早期的 Lua GC 采用的是 stop the world 的实现。一旦发生 gc 就需要等待整个 gc 流程走完。(STW: 在垃圾回收期间除了垃圾回收器线程,其他线程都会被挂起)
如果mark阶段一次性把所有节点都扫描,再一次性清理完,那么这两个步骤就都很简单了。但是,这样就有效率问题,一次性要把所有对象处理一遍,在大工程里面就绝对是一个瓶颈。
所以,lua5.0以后就把gc改成了增量式的gc,主要是把标记扩展成了三种颜色,下面详细介绍一下。
我们可以将所有对象分成三个状态:
White状态,也就是待访问状态。表示对象还没有被垃圾回收的标记过程访问到。
Gray状态,也就是待扫描状态。表示对象已经被垃圾回收访问到了,但是对象本身对于其他对象的引用还没有进行遍历访问。
Black状态,也就是已扫描状态。表示对象已经被访问到了,并且也已经遍历了对象本身对其他对象的引用。
将root集合引用到的对象从White设置成Gray,并放到Gray集合中;
while(Gray集合不为空,并且没有超过本次计算量的上限)
{
从Gray集合中移除一个对象O,并将O设置成Black状态;
for(O中每一个引用到的对象O1) {
if(O1在White状态) {
将O1从White设置成Gray,并放到到Gray集合中;
}
}
}
for(任意一个对象O){
if(O在White状态)
销毁对象O;
else
将O设置成White状态;
}
但是由于垃圾回收的过程变成分步的话,那么我们之前已经标注到的状态就可能会发生改变,此时lua提供了屏障barrier在程序正常运行过程中,监控所有的引用改变,然后更换对象的状态。
好,今天的分享就到这里,祝各位功力渐长平步青云,谢谢大家~~