>Lua的垃圾收集器只能回收那些它认为是垃圾的东西,不会回收用户认为是垃圾的东西。比如栈,栈由一个数组和一个顶部索引实现,如果弹出一个元素仅仅递减顶部索引,那么仍然停留在数组中的顶部对象,对Lua来说就不是垃圾。又比如存储在全局变量中的对象,即使程序不会再用它们,对Lua来说也不是垃圾。在这两种情况下,用户必须对对象变量赋值nil(t[i] = nil),才能释放它们。
>有时候,简单地清楚引用肯能够还不够,需要程序和gc进行更多的协作。例如,当一个对象处于数组中时,即使当前其他地方没有使用它,只要数组仍引用着它,它就无法被回收。也就是说,我们不想用t[i] = nil,也可以回收除了t引用外没有引用的对象。
>弱引用table(weak table)就是这么一种机制,用户用它告诉Lua这个table的引用不应该阻碍一个对象的回收。如果一个对象的所有引用都来自弱引用table,那么gc就可以回收这个对象了,并可以以某种形式来删除这些弱引用本身。
>table中有key和value,这两者都可以包含任意类型的对象。有3种弱引用table:具有弱引用key的table、具有弱引用value的table、同时具有弱引用key和弱引用value的table。无论哪种类型的弱引用table,只要有一个key或value被回收,那么它对应的整个key-value对都会从table中删除。
>table的弱引用类型通过元表中的__mode字段决定的,__mode是一个字符串,如果包含'k',key是弱引用的,如果包含'v',value是弱引用的,如果同时包含'k'和'v',key和value都是弱引用的。
a = {}
setmetatable(a, { __mode = "k" })
key = {}
a[key] = 1
key = {}
a[key] = 2
collectgarbage() -- 强制进行垃圾回收
for k, v in pairs(a) do print(v) end --> 2
第二个key值把第一个key值覆盖了,导致第一个key值没有被引用,导致强制垃圾回收时,被收回了。
key = nil
collectgarbage()
for k, v in pairs(a) do print(v) end
-->无输出,此时key变量也没有引用a原来的第二个key值,也导致被回收了。
>Lua只会回收弱引用table中的对象,像数字和布尔这样的“值”是不可回收的,字符串有些特殊,从实现的角度看,字符串是可以回收的,但从程序员的角度看,字符串就是值,而非对象,和数字和布尔一样,是不可回收的。
>备忘录(memoire)函数:
-- 用空间换时间,loadstring的时间成本很高,用一个辅助table记录下所有调用loadstring的结果。
local results = {}
function mem_loadstring (s)
local res = results[s]
if res == nil then --是否记录过
res = assert(loadstring(s))
results[s] = res -- 备以后用
end
return res
end
--改进:节省的时间非常客观,但results可能会把内存耗掉,弱引用table可以解决这个问题,让results具有弱引用的value即可。
local results = {}
setmetatable(results, { __mode = "v" }) -- 由于key是字符串,对弱引用table没有影响,所以完全可以用"kv"代替"v",不过有点画蛇添足了。
function mem_loadstring (s)
local res = results[s]
if res == nil then
res = assert(loadstring(s))
results[s] = res
end
return res
end
>“备忘录”技术还可以用来确保某类对象的唯一性。
function createRGB (r, g, b)
return { red = r, green = g, blue = b }
end
--改进:复用具有相同颜色的table,备忘录table的key根据颜色分量生成。
local results = {}
setmetatable(results, { __mode = "v" })
function createRGB (r, g, b)
local key = r .. "-" .. g .. "-" .. b
local color = results[key]
if color == nil then
color = { red = r, green = g, blue = b }
results[key] = color -- 备以后用
end
return color
end
>弱引用table还可以将对象与属性关联起来。当对象没有其他引用时,Lua可以回收时,弱引用table中对应的键值对也会删除。
--用弱引用table将每个table与其默认值关联起来(第13章“元表与元方法”有提)。
local defaults = {}
setmetatable(defaults, { __mode = "k" })
local mt = { __index = function (t) return defaults[t] end {
function setDefault (t, d)
defaults[t] = d
setmetatable(t, mt)
end
--重复的默认值复用同样的元表,不同的默认值使用不同的元表(第13章“元表与元方法”有提)。
local metas = {}
setmetatable(metas, { __mode = "v" })
function setDefaults (t, d)
local mt = metas[d]
if mt == nil then
mt = { __index = function () return d end}
metas[d] = mt -- 备以后用
end
setmetatable(t, mt)
end
第一种做法为每个table的默认值(defaults中的一个键值对)使用内存,第二种做法为每个不同的默认值(一个新table、一个新closure(if语句会产生closure???)和metas中的一个键值对)使用一组内存,如果程序有上千个table和一些默认值,采用第二种做法,如果只有很少的table或table的默认值很少相同的,采用第一种做法。