一直到 Lua5.0
, Lua
语言使用的都是一个简单的标记-清除式垃圾收集器。这种垃圾收集器又被称为" stop-the-world
(全局暂停)"式的收集器,意味着 Lua
语言会时不时地停止主程序的运行来执行一次完整的垃圾收集周期。每一个垃圾收集周期由四个阶段组成:标记( mark
)、清理( cleaning
)、清除( sweep
)和析构( finalization
)。
标记阶段把根节点集合( root set
)标记为活跃,根节点集合由 Lua
语言可以直接访问的对象组成。在 Lua
语言中,这个集合只包括C注册表
保存在一个活跃对象中的对象是程序可达的,因此也会被标记为活跃(当然,在弱引用表中的元素不遵循这个规则)。当所有可达对象都被标记为活跃后,标记阶段完成。
在开始清除阶段前, Lua
语言先执行清理阶段,在这个阶段中处理析构器和弱引用表。首先, Lua
语言遍历所有被标记为需要进行析构、但又没有被标记为活跃状态的对象。这些没有被标记为活跃状态的对象会被标记为活跃(复苏, resurrected
),并被放在一个单独的列表中,这个列表会在析构阶段用到。然后, Lua
语言遍历弱引用表并从中移除键或值未被标记的元素。
清除阶段遍历所有对象(为了实现这种遍历, Lua
语言把所有创建的对象放在一个链表中)。如果一个对象没有被标记为活跃, Lua
语言就将其回收。否则, Lua
语言清理标记,然后准备进行下一个清理周期。
最后,在析构阶段, Lua
语言调用清理阶段被分离出的对象的析构器。
使用真正的垃圾收集器意味着 Lua
语言能够处理对象引用之间的环。在使用环形数据结构时,我们不需要花费额外的精力,他们会像其他数据一样被回收。
Lua5.1
使用了增量式垃圾收集器( incremental collector
)。这种垃圾收集器像老版的垃圾收集器一样执行相同的步骤,但是不需要在垃圾收集期间停止主程序的运行。相反,它与解释器一起交替运行。每当解释器分配了一定数量的内存时,垃圾收集器也执行一小步(这意味着,在垃圾收集工作期间,解释器可能会改变一个对象的可达性。为了保证垃圾收集器的正确性,垃圾收集器中的有些操作具有发现危险改动和纠正所涉及对象标记的内存屏障( barrier
))。
Lua5.2
引入了紧急垃圾收集( emergency collection
)。当内存分配失败时, Lua
语言会强制进行一次完整的垃圾收集,然后再次尝试分配。这些紧急情况可以发生在 Lua
语言进行内存分配的任意时刻,包括 Lua
语言处于不一致的代码执行状态时,因此,这些垃圾收集动作不能运行析构器。