Lua垃圾回收

2.4k 词

撰写于 2018-05-25

    <span id="post-title-updated">修改于 2018-05-25</span>
  
  
  <span id="post-title-categories">分类
  
  
    
    
    <a href="/categories/高级Lua/">高级Lua</a>
  
  </span>
  
  
  <span id="post-title-tags">
  标签
  
  
    
    
    <a href="/tags/Lua/">Lua</a>
  
  </span>
  
</p>

<p>本来不想写这篇的,往浅了说,没啥意思,就是api的使用方法,往深了说,就是lua自动GC的原理,涉及到底层源码的解读,暂时没精力看。那就折中一下,说一下api的使用方法和GC的基本原理,不涉及C源码部分。</p>

Lua 垃圾回收原理

lua的垃圾回收算法称为“mark-and-sweep”,标记回收。算法本身不复杂。
首先,系统管理着所有已经创建了的对象。每个对象都有其他对象的引用。root集合代表着已知的系统级别的对象引用。我们从root集合出发,就可以访问到系统引用的所有对象。而没有访问到的对象就死垃圾对象,需要被销毁。
所有对象分成三个状态:

  1. White状态,也就是待访问的状态。表示对象还没有被GC的标记过程访问到。
  2. Gray状态,也就是待扫描的状态。表示对象已经被GC访问到,但是对象本身包含的对其他对象的引用还没有进行遍历访问。
  3. Black状态,也是已经扫描的状态。也就是该对象已经被GC访问到,并且也已经便利了对象本省包含的对其他对象的引用。
    基本思想课描述如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    当前所有对象都是White状态;  
    将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状态;
    }

但是运行GC需要消耗时间,并不是一直在执行,如果在对象很多的情况下,会影响程序的执行。解决的方法就是,将GC分步执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
当前所有对象都是White状态;  
将root集合引用到的对象从White设置成Gray,并放到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状态;
}

但是上面的执行会有一个问题,由于程序可以正常执行,所以会破坏当前对象之间的引用关系。比如说当程序的改变,使得一个标记为black的对象引用到了一个white对象,就会造成回收错误!解决这个问题的办法就是设置barrier。barrier在程序正常运行过程中,监控所有的引用改变。如果一个black对象需要引用一个white对象,存在两种处理办法:

  1. 将white对象设置成gray,并添加到gray列表中等待扫描。这样等于帮助整个GC的标识过程向前推进了一步。
  2. 将black对象该回成gray,并添加到gray列表中等待扫描。这样等于使整个GC的标识过程后退了一步。
    这种垃圾回收方式被称为”Incremental Garbage Collection”(简称为”IGC”,Lua所采用的就是这种方法。使用”IGC”并不是没有代价的。IGC所检测出来的垃圾对象集合比实际的集合要小,也就是说,有些在GC过程中变成垃圾的对象,有可能在本轮GC中检测不到。不过,这些残余的垃圾对象一定会在下一轮GC被检测出来,不会造成泄露。

Lua 垃圾回收函数

Lua垃圾回收只有一个函数collectgarbage,可以根据传入的参数,实现不同的功能。

  1. collectgarbage(“collect”): 执行垃圾回收的一个完整周期。
  2. collectgarbage(“count”): 返回当前使用的千字节的程序内存量
  3. collectgarbage(“restart”): 如果垃圾收集器已经停止,将重新启动它。
  4. collectgarbage(“setpause”): 第二个参数/100代表在开始一个新的收集周期之前要等待多久。当这个值小于等于100的时候,就代表执行完一个周期之后不会等待,直接进入下一个周期。当这个值为200的时候,就代表当内存达到上一个周期结束时的两倍的时候,再进入下一个周期。
  5. collectgarbage(“setstepmul”): 第二参数/100代表单步的速度,默认值为200,代表是内存分配速度的两倍。
  6. collectgarbage(“step”): 运行垃圾回收的一步。第二个参数是越大step也会变大。在收集的垃圾将返回true,如果触发的步骤是一个垃圾收集周期的最后一步。
  7. collectgarbage(“stop”): 停止垃圾收集器,如果它的运行。

总结:lua有自动垃圾回收的机制,如果不是特殊要求,无需手动设置和回收。