Lua学习笔记 第十七章 弱引用table

Lua采用了自动内存管理机制。一个程序只需创建对象,而无需删除对象。通过使用垃圾

收集机制,Lua会自动地删除那些已成为垃圾的对象。这减轻了程序员在内存管理方面的

负担,更重要的是将程序员从许多内存的bug中(如无效指针、内存泄露)中解放出来。

垃圾收集器只能回收那些它认为是垃圾的东西,它不会回收那些用户认为是垃圾的东西。

如递减顶部索引后,之前的栈顶元素仍保留在数组中;存储在全局变量中的对象。这两种

情况需要用户手动将这些对象变量赋值为nil,才能释放它们。

弱引用table机制——用户能用它来告诉Lua一个引用不应该阻碍一个对象的回收,这样垃圾

收集器就会忽视这个对象的引用,从而达到回收这个对象的目的。

Lua用弱引用table来实现弱引用,一个弱引用table就是一个具有弱引用条目的table。

如果一个对象只被一个弱引用table所持有,那么Lua就会回收这个对象。

 

table中的key和value,都可以包含任意类型的对象,它们都是强引用,它们会阻止对

其所引用对象的回收。一个弱引用table中,key和value是可以回收的。有3种弱引用的

table: 具有弱引用的key的table、具有弱引用value的table、同时具有两种弱引用的

table。不论是哪种类型的弱引用table,只要一个key或value被回收了,那么它所在的

整个条目都会从table中删除。

一个table中的弱引用类型是通过元表中的__mode字段来决定的。这个字段的值应为一个

字符串,如果这个字符串中包含字母k,那么这个table的key是弱引用的;如果包含字母

v,那么这个table的value是弱引用的。如示例:

a = {}

b = {__mod = "k"}

setmetatable(a, b)  --现在'a'的key就是弱引用

key = {}            -- 创建第一个key

a[key] = 1

key = {}            -- 创建第二个key

a[key] = 2

collectgarbage()    --强制进行一次垃圾收集

for k, v in pairs(a) do print(v) end    -->     2

本例中,第二句赋值key={}会覆盖第一个key。当收集器运行时,由于没有其它地方在引用

第一个key,因此第一个key就被回收了,并且table中相应的条目也被删除了。而第二个key,

变量key仍引用着它,因此没有被回收。

注意: Lua只会回收弱引用table中的对象。而像数字、布尔、字符串这样的值在作为key时是

不可回收的。除非它们关联的value被回收了,那么整个条目都会从这个弱引用table中删除。

 

17.1 备忘录(memoize)函数

备忘录函数是一项通用的编程技术,用空间来换取时间,来提高函数或程序的运行速度。

示例一:

每当服务器收到一个请求,它就要对代码字符串调用loadstring,然后再调用编译好的

函数。但loadstring是一个昂贵的函数,而有些发给服务器的命令具有很高的频率,如

"closeconnection()"。这时可以让服务器用一个辅助的table记录下所用调用loadstring

的结果,在每次调用loadstring前,服务器先检查这个table中是否已记录了代码字符串

编译后的结果。如果没有再调用loadstring,并将结果存储到table中。

这项优化节省的时间非常可观。但也会导致不易察觉的开销。如有些命令会重复出现,

有些命令只发生一次。而辅助table中会保存服务器收到的所有命令及其编译结果。

经过一段时间的累积后就会耗费服务器的内存。而弱引用的table正好可以解决这个问题。

local results = {}

setmetatable(results, {__mode="v"}) -- 使value成为弱引用

function mem_loadstring(s)

    local res =results[s]

    if res == nilthen

        res = assert(loadstring(s))

        results[s]= res

    end

    return res

end

示例二:

通过备忘录技术,确保某类对象的唯一性,以复用具有相同颜色的table。

local results = {}

setmetatable(results, {__mode = "v"})   -- 使value成为弱引用

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

 

17.2 对象属性

关于弱引用table的另一项重要应用是将属性与对象关联起来。当然,使用外部table来关联

它们是一种理想的做法。可以将对象作为这个外部table的key,对象的属性作为这个外部table的value。

这个外部table可以保存任意对象的属性,当然Lua也允许将任何对象作为table的key。

但是这种做法有一个重大的缺陷。当用户将一个对象作为外部table的key时,就是引用了它。

Lua是无法回收一个作为table key的对象。这时用户就可以使用弱引用table来解决这个问题。

当然本例需要的只是弱引用key。当一个弱引用key没有其它引用时,Lua就会回收它。

 

17.3 回顾table的默认值

13.4.3节讨论了如何实现具有非nil默认值的table。其中注明了还有两种技术需要弱引用

table的支持。这里将介绍两种用于默认值的技术,它们其实是上述备忘录和对象属性的特殊应用。

第一种做法是使用一个弱引用table,通过它将每个table与其默认值关联起来:

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

注--如果defaults没有弱引用key,它就会使所有具有默认值得table持久存在下去。

 

第二种做法是对每种不同的默认值使用不同的元表。不过,只要有重复的默认值,就

复用同样的元表。这是备忘录的典型应用:

local metas = {}

setmetatable(metas, {__mode = "v"})

function setDefault(t, d)

    local mt =metas[d]

    if mt == nilthen

        mt ={__index = function() return d end}

        meata[d]= mt

    end

    setmetatable(t,mt)

end

注--这里用到弱引用value,这样当metas中的元表在不使用时就可以被回收了。

 

这两种实现具有类似的复杂度和性能表现。第一种做法需要为每个table的默认值(defaults中的一个条目)

使用内存。而第二种做法则需要为每种不同的默认值使用一组内存(一个新table,一个新closure和metas中

的一个条目)。当程序中有上千个table和一些默认值时,第二种做法是首选;但如果只有很少的table共享

几个公用的默认值时,那么就应该选择第一种做法。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值