Lua的table为table的key和value提供了一种weak的机制,即如果当前的key或/和value不再被除此table以外的任意对象引用时,将被标记为可被lua的垃圾回收器回收的对象。使用weak table,需要设置table的metatable的__mode属性,可以设置为"k","v","kv",分别表示对key还是value进行weak处理。例如:
local t = {}
local mt = { __mode = "kv" }
setmetatable(t, mt)
local x = {}
local y = {}
t[x] = 1
t[1] = y
x = nil
y = nil
collectgarbage()
for k, v in pairs(t) do
print(k, v)
end
运行后,我们发现t变成了一张空表。不过需要注意这里手动调了一下lua的gc回收,不然可能因为还没触发gc,导致t还是有值的。
另外,weak table的key和value只对object有效,像string和number这样的类型是无法生效的。例如:
local t = {}
local mt = { __mode = "kv" }
setmetatable(t, mt)
local x = "x"
local y = "y"
t[x] = 1
t[1] = y
x = nil
y = nil
collectgarbage()
for k, v in pairs(t) do
print(k, v)
end
运行之后table还是原来的样子。
lua 5.2 后引入了 ephemeron table 这个特性,这是为了防止循环引用导致无法回收weak table中的key和value设置的。例如:
local t = {}
local mt = { __mode = "kv" }
setmetatable(t, mt)
local x = {}
local y = {}
x.ref = y
y.ref = x
t[x] = 1
t[1] = y
x = nil
y = nil
collectgarbage()
for k, v in pairs(t) do
print(k, v)
end
从代码可以看出,虽然外界没有地方再去引用table x和table y了,但是这两个table实际上是相互引用的。ephemeron table就是为了解决这个问题而存在的。即,只有weak table的key和value拥有外部的强引用,才不会被回收。这里可以参考云风的这篇博客。
有时候,我们希望对象在被gc的时候能够额外做一些事情,这里lua提供了__gc元方法:
local t = {}
t.val = 1
local mt = { __gc = function(t) print("gc val ", t.val) end }
setmetatable(t, mt)
collectgarbage()
for k, v in pairs(t) do
print(k, v)
end