第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于后续版本,但有一些差异。
第四版针对 Lua 5.3,可在 Amazon 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目


17 – 弱表

Lua 具有自动内存管理功能。程序只创建对象(表、函数等);没有用于删除对象的功能。Lua 使用垃圾回收自动删除变成垃圾的对象。这使您免除了大部分内存管理负担,更重要的是,免除了与该活动相关的大部分错误,例如悬空指针和内存泄漏。

与其他一些回收器不同,Lua 的垃圾回收器不会出现循环问题。使用循环数据结构时,您无需执行任何特殊操作;它们会像任何其他数据一样被回收。不过,有时即使是更智能的回收器也需要您的帮助。没有垃圾回收器能让您忘记所有内存管理问题。

垃圾回收器只能回收它能确定是垃圾的内容;它无法知道您认为什么是垃圾。一个典型的例子是堆栈,它使用数组和指向顶部的索引来实现。您知道数组的有效部分只到顶部,但 Lua 不知道。如果您通过简单地递减顶部来弹出元素,则留在数组中的对象对 Lua 来说不是垃圾。同样,存储在全局变量中的任何对象对 Lua 来说都不是垃圾,即使您的程序永远不会再次使用它。在这两种情况下,由您(即您的程序)将 nil 分配给这些位置,以便它们不会锁定其他空闲对象。

但是,仅仅清理引用并不总是够的。一些构造需要您和回收器之间进行额外的协作。一个典型的例子发生在您希望在程序中保留某种所有活动对象(例如文件)的集合时。这似乎是一项简单的任务:您所要做的就是将每个新对象插入集合中。但是,一旦对象进入集合,它就永远不会被回收!即使没有其他人指向它,集合也会指向它。Lua 无法知道此引用不应阻止回收对象,除非您告诉 Lua。

弱表是用来告诉 Lua 引用不应阻止对象回收的机制。弱引用是对垃圾回收器不考虑的对象的引用。如果指向对象的引用都是弱引用,那么该对象将被回收,并且以某种方式删除这些弱引用。Lua 将弱引用实现为弱表:弱表是其中所有引用都是弱引用的表。这意味着,如果某个对象仅保存在弱表中,那么 Lua 最终会回收该对象。

表具有键和值,两者都可能包含任何类型的对象。在正常情况下,垃圾回收器不会回收显示为可访问表的键或值的对象。也就是说,键和值都是引用,因为它们阻止了对它们引用的对象的回收。在弱表中,键和值可能是弱的。这意味着有三种类型的弱表:具有弱键的表、具有弱值的表以及完全弱的表,其中键和值都是弱的。无论表类型如何,当键或值被回收时,整个条目都会从表中消失。

表的弱性由其元表的字段 __mode 给出。此字段的值(如果存在)应为字符串:如果字符串包含字母 `k´(小写),则表中的键是弱的;如果字符串包含字母 `v´(小写),则表中的值是弱的。以下示例虽然是人为的,但说明了弱表的基本行为

    a = {}
    b = {}
    setmetatable(a, b)
    b.__mode = "k"         -- now `a' has weak keys
    key = {}               -- creates first key
    a[key] = 1
    key = {}               -- creates second key
    a[key] = 2
    collectgarbage()       -- forces a garbage collection cycle
    for k, v in pairs(a) do print(v) end
      --> 2
在此示例中,第二个赋值 key = {} 覆盖了第一个键。当回收器运行时,没有其他引用指向第一个键,因此它被回收,并且表中相应的条目被删除。然而,第二个键仍然锚定在变量 key 中,因此它不会被回收。

请注意,只有对象可以从弱表中回收。值(例如数字和布尔值)不可回收。例如,如果我们在表 a(来自我们前面的示例)中插入一个数字键,那么它将永远不会被回收器删除。当然,如果与数字键对应的值被回收,那么整个条目将从弱表中删除。

字符串在此处表现出微妙之处:尽管字符串是可回收的,但从实现的角度来看,它们与其他可回收对象不同。其他对象(例如表和函数)是显式创建的。例如,每当 Lua 评估 {} 时,它都会创建一个新表。每当它评估 function () ... end 时,它都会创建一个新函数(实际上是一个闭包)。然而,当 Lua 评估 "a".."b" 时,它是否会创建一个新字符串?如果系统中已经存在字符串 "ab" 怎么办?Lua 是否会创建一个新字符串?编译器是否可以在运行程序之前创建该字符串?这并不重要:这些是实现细节。因此,从程序员的角度来看,字符串是值,而不是对象。因此,与数字或布尔值一样,字符串不会从弱表中删除(除非其关联值被回收)。