最近看tolua的协同,它里面有用到一个__mode 的字段,如下所示:
local comap = {}
local pool = {}
setmetatable(comap, {__mode = "kv"})
好奇他是干什么用的就去了解了一下。
原来他就是lua的弱引用,是一个用来优化垃圾回收器进行垃圾回收的机制。
1、首先了解一下lua的垃圾回收机制
Lua垃圾回收机制
Lua 采用了自动内存管理。 这意味着你不用操心新创建的对象需要的内存如何分配出来, 也不用考虑在对象不再被使用后怎样释放它们所占用的内存。
Lua 运行了一个垃圾收集器来收集所有死对象 (即在 Lua 中不可能再访问到的对象)来完成自动内存管理的工作。
我们可以通过设置垃圾收集器间歇率和垃圾收集器步进倍率两个参数来控制垃圾收集器工作的频率和速度。
- 垃圾收集器间歇率控制着收集器需要在开启新的循环前要等待多久。增大这个值会减少收集器的积极性。 当这个值比 100 小的时候,收集器在开启新的循环前不会有等待。 设置这个值为 200 就会让收集器等到总内存使用量达到 之前的两倍时才开始新的循环。
- 垃圾收集器步进倍率控制着收集器运作速度相对于内存分配速度的倍率。 增大这个值不仅会让收集器更加积极,还会增加每个增量步骤的长度。 不要把这个值设得小于 100 , 那样的话收集器就工作的太慢了以至于永远都干不完一个循环。 默认值是 200 ,这表示收集器以内存分配的"两倍"速工作。
Lua 提供了以下函数collectgarbage ([opt [, arg]])用来控制自动内存管理:
collectgarbage(“collect”): 做一次完整的垃圾收集循环。通过参数 opt 它提供了一组不同的功能:
collectgarbage(“count”): 以 K 字节数为单位返回 Lua 使用的总内存数。
collectgarbage(“restart”): 重启垃圾收集器的自动运行。
collectgarbage(“setpause”): 将 arg 设为收集器的 间歇率 (参见 §2.5)。 返回 间歇率 的前一个值。
collectgarbage(“setstepmul”): 返回 步进倍率 的前一个值。
collectgarbage(“step”): 单步运行垃圾收集器。 步长"大小"由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 true 。
collectgarbage(“stop”): 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。
2、当了解了垃圾回收机制的基本原理,我们就可以开始聊聊弱引用了
2.1、为什么会有弱引用?
lua会自动去删除那些已经成为垃圾的对象。但问题在于,垃圾回收器只能够回收那些它认为是垃圾的东西。比如那些存储在全局变量中的对象table,即使程序不会再使用到他,lua也不会认为他们是垃圾,就需要用户自行将这些变量对象赋值为nil,来得以释放.
用户能用他来告诉Lua一个引用不应该阻碍一个对象的回收,所谓的弱引用,实际上就是一种会被垃圾收集器忽视的引用.
2.2、什么是弱引用
弱引用 指内部元素为 弱引用 的表。 垃圾收集器会忽略掉弱引用。 换句话说,如果一个对象只被弱引用引用到, 垃圾收集器就会回收这个对象。这是参考手册上面上面的描述。
2.3、怎么样用
一个table的弱引用类型是通过其元表中的__mode来决定的,这是一个字符串,字母k代表table的key是弱引用,字母v代表table的value是弱引用,也可以是key 和vaule.
2.3.1、key是弱引用
a={}
b={__mode=“k”}
2.3.2、value是弱引用
a={}
b={__mode=“v”}
2.3.3、key 和vaule是弱引用
a={}
b={__mode=“kv”}
无论哪种类型的弱引用table,只要有一个key或value被回收,整个条目都会被删除;
来看具体例子吧。
a、当我们普通不设置vaule为弱引用时,我们强制垃圾回收,a[1]的值是一个引用类型table是不能被回收的
a={}
b={}
setmetatable(a,b) --a的key为弱引用
a[1]= { 1,2};
a[2]=2;
collectgarbage() --强制进行一次垃圾收集
for k,v in pairs(a) do
gameTools.errorLog("k="..tostring(k).."v="..tostring(v))
end
输出为
b、当我们设置vaule为弱引用时
a={}
b={__mode="v"}
setmetatable(a,b) --a的key为弱引用
a[1]= { 1,2};
a[2]=2;
collectgarbage() --强制进行一次垃圾收集
if a then
for k,v in pairs(a) do
gameTools.errorLog("k="..tostring(k).."v="..tostring(v))
end
end
输出为
a[1]的值是一个table,但是垃圾收集器会忽略掉这个引用,依旧会回收他。
Lua 的弱引用表只对对象有效果,对于字符串、数值、布尔值都是无效的,
c、关于key的弱引用
a={}
b={__mode="k"}
setmetatable(a,b) --a的key为弱引用
a[{1}]=1;
local key_2={} --创建第二个key
a[key_2]=2
a[1]={"I am a table"};
a[5]=5;
collectgarbage() --强制进行一次垃圾收集
if a then
for k,v in pairs(a) do
gameTools.errorLog("k="..tostring(k).."v="..tostring(v))
end
end
输出
这里有个小细节就是为什么value=2的key没有删除掉,而vaule=1的key被回收了,是因为local key_2还在引用vaule=2的key的table.所以垃圾收集器会判定这个有其他变量引用它,就不会回收他。而vaule=1那个的key的tabel没有任何变量引用它,我们又设置了弱引用,所以垃圾回收器会回收掉这个key.
d、还有一种 b={__mode=“kv”},
kv是二者的组合,任何情况下只要key和value中的一个被垃圾收集器自动回收,那么kv键值对就被从表中移除。
参考链接:https://www.jianshu.com/p/d9b03481a9f6
参考链接:https://www.jishuchi.com/read/lua-lang/2396