垃圾回收教程 |
|
在像 C 和 C++ 这样的语言中,我们在运行时创建的对象(例如使用 malloc()
或 new
)必须显式删除。内存泄漏是由于对象被分配、丢失且从未释放而导致的。可以编写内存跟踪系统,为我们分配内存并知道我们何时不再需要它。一些语言内置了此功能,包括 Lua。
有各种方法来跟踪哪些内存对象不再需要。一旦应用程序的变量不再引用某个对象,该对象就被认为是不可达的。不再可达的对象将变得可删除,并且是自动处理或垃圾回收的候选者。
在以下示例中,我们创建了一些 Lua 对象并将它们分配给变量。如果我们将其他值分配给变量,并且没有其他变量引用先前的对象,它们将变得不可达。如果没有垃圾回收,这会导致内存泄漏。例如:
> t = { this="table"; 1,2,3 } -- construct a table and assign it to "t" > t = nil -- set "t" to nil and the table becomes unreachable > > s = "a string" -- create string variable > s = "another string" -- when set to another value the old value becomes unreachable
有各种类型的收集器,以及与垃圾回收相关的许多术语 [1]。最简单的收集算法之一是引用计数。在这里,我们只在分配的每个对象中保留一个计数,表示引用(或使用)它的对象数量。如果引用另一个对象的物体数量降至零,它将变得不可达(并且未使用),并且可以释放。这方面的一个小问题是循环引用,其中对象 A 引用对象 B,而 B 引用 A。只要其他对象引用 A 和/或 B,这就可以了,但当它们不再这样做时,A 和 B 将形成一个岛屿。由于循环引用,它们现在成为一组不可达且相互永久的对象。除非扩展引用计数收集算法,否则该岛屿永远不会被释放。例如:
> a = {} > b = {} > a['other'] = b > b['other'] = a -- now we have a cyclic reference > a,b = nil,nil -- oh dear, we don't have a reference to the 2 tables now > -- there are two tables which are unreachable
为了从上述算法中消除岛屿,我们可以遍历系统中的所有变量,并查看它们引用了哪些对象。如果变量发现某些对象不可达,我们可以对其进行垃圾回收。这种算法称为标记和清除,即我们标记所有可达对象并清除剩余的对象。Lua 专门使用标记和清除垃圾回收算法。这样做的好处是我们不必对对象进行引用计数,因此不会出现循环引用问题。缺点是该算法需要时间来处理,并且在实时应用程序中可能是一个问题。
注意:如果我们要分布式处理标记和清除收集算法,使其不一次完成,那么它对主机系统将更加透明。这称为增量垃圾回收。此更改是在 Lua 版本 5.1 中进行的。
Lua 提供了一些函数来控制和查询垃圾回收的状态。更多细节请参见 CoreFunctionsTutorial。
collectgarbage(opt [, arg])
[2] 是垃圾回收器的接口。
参见参考手册第 2.10 节 [3]。
维基上的其他页面更详细地介绍了 Lua 中的垃圾回收。以下建议阅读