第17章 Weak表

Lua自动进行内存的管理。程序只能创建对象(表,函数等),而没有执行删除对象的函数。通过使用垃圾收集技术,Lua会自动删除那些失效的对象。这可以使你从内存管理的负担中解脱出来。更重要的,可以让你从那些由此引发的大部分BUG中解脱出来,比如指针挂起(dangling pointers)和内存溢出。

和其他的不同,Lua的垃圾收集器不存在循环的问题。在使用循环性的数据结构的时候,你无须加入特殊的操作;他们会像其他数据一样被收集。当然,有些时候即使更智能化的收集器也需要你的帮助。没有任何的垃圾收集器可以让你忽略掉内存管理的所有问题。

垃圾收集器只能在确认对象失效之后才会进行收集;它是不会知道你对垃圾的定义的。一个典型的例子就是堆栈:有一个数组和指向栈顶的索引构成。你知道这个数组中有效的只是在顶端的那一部分,但Lua不那么认为。如果你通过简单的出栈操作提取一个数组元素,那么数组对象的其他部分对Lua来说仍然是有效的。同样的,任何在全局变量中声明的对象,都不是Lua认为的垃圾,即使你的程序中根本没有用到他们。这两种情况下,你应当自己处理它(你的程序),为这种对象赋nil值,防止他们锁住其他的空闲对象。

然而,简单的清理你的声明并不总是足够的。有些语句需要你和收集器进行额外的合作。一个典型的例子发生在当你想在你的程序中对活动的对象(比如文件)进行收集的时候。那看起来是个简单的任务:你需要做的是在收集器中插入每一个新的对象。然而,一旦对象被插入了收集器,它就不会再被收集!即使没有其他的指针指向它,收集器也不会做什么的。Lua会认为这个引用是为了阻止对象被回收的,除非你告诉Lua怎么做。

Weak表是一种用来告诉Lua一个引用不应该防止对象被回收的机制。一个weak引用是指一个不被Lua认为是垃圾的对象的引用。如果一个对象所有的引用指向都是weak,对象将被收集,而那些weak引用将会被删除。Lua通过weak tables来实现weak引用:一个weak tables是指所有引用都是weaktable。这意味着,如果一个对象只存在于weak tables中,Lua将会最终将它收集。

表有keysvalues,而这两者都可能包含任何类型的对象。在一般情况下,垃圾收集器并不会收集作为keysvalues属性的对象。也就是说,keysvalues都属于强引用,他们可以防止他们指向的对象被回收。在一个weak tables中,keysvaules也可能是weak的。那意味着这里存在三种类型的weak tablesweak keys组成的tablesweak values组成的tables;以及纯weak tables类型,他们的keysvalues都是weak的。与table本身的类型无关,当一个keys或者vaule被收集时,整个的入口(entry)都将从这个table中消失。

表的weak性由他的metatable__mode域来指定的。在这个域存在的时候,必须是个字符串:如果这个字符串包含小写字母‘k’,这个table中的keys就是weak的;如果这个字符串包含小写字母‘v’,这个table中的vaules就是weak的。下面是一个例子,虽然是人造的,但是可以阐明weak tables的基本应用:

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的值。当垃圾收集器工作时,在其他地方没有指向第一个key的引用,所以它被收集了,因此相对应的table中的入口也同时被移除了。可是,第二个key,仍然是占用活动的变量key,所以它不会被收集。

要注意,只有对象才可以从一个weak table中被收集。比如数字和布尔值类型的值,都是不会被收集的。例如,如果我们在table中插入了一个数值型的key(在前面那个例子中),它将永远不会被收集器从table中移除。当然,如果对应于这个数值型keyvaule被收集,那么它的整个入口将会从weak table中被移除。

关于字符串的一些细微差别:从上面的实现来看,尽管字符串是可以被收集的,他们仍然跟其他可收集对象有所区别。 其他对象,比如tables和函数,他们都是显示的被创建。比如,不管什么时候当Lua遇到{}时,它建立了一个新的table。任何时候这个 function()。。。end建立了一个新的函数(实际上是一个闭包)。然而,Lua见到“a..b”的时候会创建一个新的字符串?如果系统中已经有一个字符串“ab”的话怎么办?Lua会重新建立一个新的?编译器可以在程序运行之前创建字符串么?这无关紧要:这些是实现的细节。因此,从程序员的角度来看,字符串是值而不是对象。所以,就像数值或布尔值,一个字符串不会从weak tables中被移除(除非它所关联的vaule被收集)。


相关链接:
lua程序设计目录 - 中国lua开发者 - lua论坛