垃圾回收

Garbage

Lua 会进行自动的内存管理。程序可以创建出对象(表、闭包等),却没有删除对象的功能。Lua 会使用 垃圾回收,garbage collection,自动删除成为了垃圾的对象。这使我们摆脱了内存管理的大部分负担,更重要的是,使我们摆脱了与内存管理相关的大部分错误,如悬空指针及内存泄漏等,dangling pointers and memory leaks。

在一种理想世界中,程序员是看不到垃圾回收器的,the garbage collector,他就像个优秀的清洁工,不会干扰其他工作人员的工作。不过,有时即使是更聪明的垃圾回收器,也需要我们的帮助。我们可能需要在某些性能关键时刻,停止他工作,或者只允许他在某些特定时间工作。此外,垃圾回收器只能回收他能确定是垃圾的东西,而不能猜度我们认为什么是垃圾。没有垃圾回收器,我们就无法摆脱资源管理方面的所有烦恼,比如内存和外部资源的囤积等,hoarding memory and external resources。

弱表,weak tables、终结器,finalizers,和函数 collectgarbage 三者,是我们在 Lua 中,帮助垃圾回收器可以使用的主要机制。弱表实现对程序仍可访问 Lua 对象的回收;终结器实现对不受垃圾回收器直接控制外部对象的回收。函数 collectgarbage 则允许我们控制垃圾回收器的运行速度。在本章中,我们将讨论这些机制。

弱表

Weak Tables

正如我们曾说过的,垃圾回收器不能猜度我们认为的垃圾是什么。典型例子就是堆栈,他是通过一个数组,以及一个指向顶部的索引实现的。我们知道,数组的有效部分只会到顶部,但 Lua 却不知道。如果我们通过简单地递减顶层索引,来弹出一个元素,那么对 Lua 来说,数组中剩下的对象就不是垃圾。同样,存储在某个全局变量中的任何对象,对 Lua 来说都不是垃圾,即使咱们的程序永远不再用到他。在这两种情况下,我们(即我们的程序)都需要为这些位置赋值 nil,这样他们就不会锁定本可丢弃的对象。

然而,只是清理咱们的引用,并非总是足够了。有的结构就需要程序和回收器之间,额外的协作。典型例子便是,当我们想要保留程序中某种存活对象(如文件)列表时。此任务看似简单:我们只需将每个新对象,插入该列表即可。但是,一旦对象成为该列表的一部分,他就永远不会被回收!即使没有其他对象指向他,列表也会指向他。除非我们告诉 Lua 没有对象指向他这一事实,否则 Lua 就无法知道,列表指向他这一引用,不应阻止该对象的回收。

弱表正是我们用来告诉 Lua,某个引用不应阻止对象回收的机制。弱引用,weak reference 是种垃圾回收器不会考虑的对象引用。如果指向某个对象的全部引用都是弱引用,那么垃圾回收器就会回收该对象,并删除这些弱引用。Lua 通过 弱表,实现的弱引用:弱表是种其条目均为弱条目的表。这意味着,如果某个对象只存在于弱表中,那么 Lua 最终将回收该对象。

表有着键与值,两者均可包含任何类型对象。正常情况下,垃圾回收器不会回收作为键或值,出现在某个可访问表中的对象。也就是说,键和值都是强引用,因为他们可以防止回收他们所引用的对象。在弱表中,键和值都可以是弱引用。这就意味着有三种弱表:键为弱的表、值为弱的表以及键和值都为弱的表。不管是哪种表,当键或值被回收时,整个条目都会从表中消失。

表的弱性,是由其元表中的 __mode 字段所给出的。如果该字段存在且值为 "k",则表中的键为弱键;如果该字段值为 "v",则表中的值为弱值;如果该字段值为 "kv",则键和值都为弱值。下面的示例虽然是杜撰的,却说明了弱表的基本行为:

a = {}
mt = {__mode = "k"}
setmetatable(a, mt)     -- 现在 'a' 就有了弱键
key = {}                -- 创建出首个键
a[key] = 1
key = {}                -- 创建出第二个键
a[key] = 2
collectgarbage()        -- 强制进行一次垃圾回收周期
for k, v in pairs(a) do print(v) end
    --> 2

在这个示例中,第二个赋值 key = {} 覆盖了第一个 key 的引用。对 collectgarbage 的调用,会强制垃圾回收器进行一次完整的回收。由于不再有其他的对第一个键引用,Lua 就会回收这个键并删除该表中的相应条目。然而,第二个键仍锚定在变量 key 中,因此 Lua 不会回收他。

请注意,只有在弱表中只有对象才能被删除。数字和布尔值等值,就不能被回收。例如,如果我们在这个表 a 中,插入一个数字键,收集器就永远不会删除他。当然,如果某个数字键对应的值,被收集到了某个有着弱值的表中,那么整个条目就会从该表中删除。

Last change: 2024-07-30, commit: 814b642