垃圾回收机制(gc):
lua使用自动内存管理机制,通过垃圾回收器来回收内存,垃圾回收器只能回收它认为是垃圾的内容,而不能回收用户认为是垃圾的内容,典型的例子---栈,栈一般用一个数组和一个表示顶部的索引值表示,如果弹出一个元素,那么仅是把栈顶索引减一,但这个元素还留在内存在被这栈数组引用着,所以垃圾回收器不知道它是垃圾,全局变量和table里的内容只能通过手动置为nil的方法来被垃圾回收器回收,因此需要一种与垃圾回收器可以协作的机制,lua里用弱引用table(weak table)来实现这个机制。
回收规则:
1.gc自动运行,也可以手动调用
2.自动收集的目标是引用计数为0的对象
3.dead objes:不能访问到的对象,没有引用指向它了,当然就是访问不到了,也就等同与垃圾内存了
弱引用table(弱表):
table的弱引用类型通过其元表中的__mode字段来决定,这个字段是一个字符串(table默认为强键强值),一张弱表可以有弱键或是弱值,也可以键值都是弱引用。 仅含有弱键的表允许收集器回收它的键,但会阻止对值所指的对象被回收。 若一张表的键值均为弱引用, 那么收集器可以回收其中的任意键和值。 任何情况下,只要键或值的任意一项被回收, 相关联的键值对都会从表中移除。
1.{__mode='k'} 包含'k'则是key的弱引用,简称弱键
2.{__mode='v'} 包含'v'则是value的弱引用,简称弱值
3.{__mode='kv'} 都包含的则key、value都是弱引用(弱键弱值)
对于普通的强引用表,当你把对象放进表中的时候,就产生了一个引用,那么即使其他地方没有对表中元素的任何引用,gc也不会被回收这些对象。那么你的选择只有两种:手动释放表元素(置为nil)或者让它们常驻内存。
strongTable = {}
strongTable[1] = function() print("i am the first element") end
strongTable[2] = function() print("i am the second element") end
strongTable[3] = {10, 20, 30}
print(table.getn(strongTable)) -- 3
collectgarbage()
print(table.getn(strongTable)) -- 3
弱键k的使用:
a = {}
b = {__mode = "k"}
setmetatable(a, b)
key = {name = "qq"} --key={}
a[key] = 1
key = {name = "ww"} --key={}
a[key] = 2
a[1] = 11 --number
a["aa"] = 22 --string
a[true] = 33 --boolean
collectgarbage() --强制进行一次垃圾收集
for k, v in pairs(a) do
if type(k) == 'table' then
print(k.name, v)
else
print(k, v)
end
end
--打印信息
1 11
true 33
ww 2
aa 22
将a表属性设置为弱键引用,key={name="qq"}相当于将key指向了{name="qq"},也可以称{name="qq"}被key所引用,随后a[key]=1,它的索引为一个table,理解为{name="qq"}对应的地址,当第二次对key进行赋值时,key由原来的指向{name="qq"}转为指向{name="ww"},这时{name="ww"}被key所引用,而原来的{name="qq"}并没有其他地方对它进行引用了,所以会被回收,所以相关联的键值会从表中移除,所以在打印的时候没有打印出{name="qq"}对应的值,但如果key是number、boolean、string类型,则不会被回收,上例中用table来当key,可以使用弱引用table来实现缓存等机制,热数据不会被回收,不用的数据自动释放。
值得注意的一点是,将上面代码中俩处key的赋值都改成key={}或者其他在数据上相等的表,也会按照相应流程进行回收,因为俩个{},也就是俩个表虽然数据上相等,但是俩个表的地址是不同的,所以在赋值之后,指向的位置是不同的,所以依然满足对应的逻辑流程。
弱值v的使用:
在编程环境中,有时你并不确定手动给一个键值赋nil的时机,而是需要等所有使用者用完以后进行释放,在释放以前,是可以访问这个键值对的。这种时候,weak表就派上用场了
weakTable = {}
weakTable[1] = function() print("i am the first element") end
weakTable[2] = function() print("i am the second element") end
weakTable[3] = {10, 20, 30}
weakTable[4] = 100 --值为number类型不会被回收
weakTable["aa"] = "aa" --值为string类型不会被回收
weakTable[true] = false --值为boolean类型不会被回收
setmetatable(weakTable, {__mode = "v"}) -- 设置为弱值表
for i, v in pairs(weakTable) do
print(i, v)
end
ele = weakTable[1] -- 给索引为1的值增加一个引用
collectgarbage() -- 第一个函数引用为1,不能gc
print("~~~~~~~~~~~~~~~~~~~~~~~~~")
for i, v in pairs(weakTable) do
print(i, v)
end
ele = nil -- 释放第一个函数的引用
collectgarbage()
print("~~~~~~~~~~~~~~~~~~~~~~~~~")
for i, v in pairs(weakTable) do
print(i, v)
end
--打印信息
1 function: 00D6B7E0
2 function: 00D6BB00
3 table: 00D69548
4 100
aa aa
true false
~~~~~~~~~~~~~~~~~~~~~~~~~
1 function: 00D6B7E0
4 100
aa aa
true false
~~~~~~~~~~~~~~~~~~~~~~~~~
4 100
aa aa
true false
当然在实际的代码过程中,我们不一定需要手动collectgarbage,因为该函数是在后台自动运行的,它有自己的运行周期和规律,对编程者来说是透明的
注意:只有拥有显示构造的对象类型会被自动从weak表中移除,值类型boolean、number是不会自动从weak中移除的。而string类型虽然也由gc来负责清理,但是string没有显示的构造过程,因此也不会自动从weak表中移除,对于string的内存管理有单独的策略。
local a = {}
setmetatable(a, {__mode = 'v'})
key = {1, 2, 3}
a[1] = key --key设置为值 此时key引用{1,2,3}
key = {4, 5, 6} --改变k的值,key引用{4,5,6}
a[2] = key
--垃圾回收
collectgarbage()
for i, v in pairs(a) do
print(i, v)
end
参考资料:
https://www.cnblogs.com/colin-chan/articles/4774651.html
https://www.cnblogs.com/sifenkesi/p/3850760.html