Lua内存是自动收集的, 这点跟Java类似, 不被任何对象或全局变量引用的数据,将被首先标记为回收,不需要开发者做任何事情.但是,正如Java也会有内存泄露一样, Lua也会有, 只不过,跟C++的不同,它是由于代码执行所装载的资源,并没有被彻底销毁而导致,其中,最臭名昭著的就是不小心把局部变量声明成了全局变量(忘了加local修饰符)。 类似这样造成的内存泄露, 跟任何其他语言的内存泄露一样,容易产生,却难以察觉, 给开发的应用带来潜在的很大隐患.
那么, 有没有一些有效的解决办法, 来解决这个这个隐患呢, 答案就是collectgarbage. collectgarbage就是开放给Lua开发人员, 用于监听Lua的内存使用情况(collectgarbage(“count”)), 同时,它还提供了collectgarbage(“collect”),允许在适当的时候进行显式的回收.
现在,通过测试代码来看看,如何玩转collectgarbage.
首先,为了有明显的对比, 先来看没有产生泄露的情况, 运行以下的test1(代码如下):
function test1( ... )
collectgarbage("collect");--为了有干净的环境,先把可以收集的其他垃圾赶走先
local c1 = collectgarbage("count")
print("最开始,Lua的内存为",c1)
local colen = {}--在这里,colen是本地变量
print("现在,声明5000个数组,并加到colen中")
for i=1,50000 do
table.insert(colen,{}) --不断的把新创建的数组,放到一个局部的变量中
end
local c2 = collectgarbage("count")
print("现在内存为:",c2)
end
test1()
运行结果如下:
最开始,Lua的内存为 23.765625
现在,声明5000个数组,并加到colen中
现在内存为: 3782.2734375
[Finished in 0.1s]
这里看到, 被local 声明的colen加了5000数组, test1调用后, 内存增加了大概300K(25906K-25620K).
现在,我们来做内存回收(调用mem函数, 代码如下):
function mem( )
print("调用GC收集..")
local c2 = collectgarbage("count")
collectgarbage("collect")
print("收集后,当前的Lua内存为",c2)
end
运行结果如下:
@@@@@调用test1
最开始,Lua的内存为 24.5205078125
现在,声明5000个数组,并加到colen中
现在内存为: 3783.0283203125
@@@@@调用test1完毕
调用GC收集..
收集后,当前的Lua内存为 3783.201171875
调用GC收集..
收集后,当前的Lua内存为 24.7705078125
调用GC收集..
收集后,当前的Lua内存为 24.76953125
调用GC收集..
收集后,当前的Lua内存为 24.767578125
[Finished in 0.1s]
( 为了保证内存的稳定,以上注意mem被调用了多次, 再第2次, 可以看到内存开始下降, 最后,大概在24.767578125K稳定下来)
好了, 从最初的3783.201171875K, 到回收后的24.767578125K, 调用多次之后,两者并没有发生变化, 也就是说,函数test1的执行,并没有产生无法回收的内存,没有泄露出现.
好了,现在运行有泄露的test2(代码如下), test2跟test1相比,只有一处不同:就是colen被误声明为全局:
function test2( ... )
collectgarbage("collect");--为了有干净的环境,先把可以收集的其他垃圾赶走先
local c1 = collectgarbage("count")
print("最开始,Lua的内存为",c1)
colen = {}--在这里,colen是本地变量
print("现在,声明5000个数组,并加到colen中")
for i=1,50000 do
table.insert(colen,{}) --不断的把新创建的数组,放到一个全局的变量中
end
local c2 = collectgarbage("count")
print("现在内存为:",c2)
end
print("@@@@@test2")
test2()
print("@@@@@test2完毕")
运行结果如下
@@@@@test2
最开始,Lua的内存为 24.4541015625
现在,声明5000个数组,并加到colen中
现在内存为: 3782.9619140625
@@@@@test2完毕
[Finished in 0.1s]
也就是说,内存也在3782.9619140625,跟test1几乎是相等, 好了,现在再调用回收(mem)函数,产生结果如下:
调用GC收集..
收集后,当前的Lua内存为 3783.197265625
调用GC收集..
收集后,当前的Lua内存为 3783.1962890625
调用GC收集..
收集后,当前的Lua内存为 3783.197265625
调用GC收集..
收集后,当前的Lua内存为 3783.1962890625
为了保证函数回收被执行,这次,总共调用了4次mem函数(看以上打印行数), 那么,从上面的结果我们看, 很不幸, 从第1次,到最后第4次, 内存都还是稳定在3783.左右, 也就是说, 跟调用test2前相比,即使Lua进行了内存回收, 内存却不会将下来 看来, 这几千K内存, 由于已放到了全局函数中,是永远没有机会被回收到了!
总结一: 如何监测Lua的编程产生内存泄露:
针对会产生泄露的函数,先调用collectgarbage(“count”),取得最初的内存使用
函数调用后, collectgarbage(“collect”)进行收集, 并使用collectgarbage(“count”)再取得当前内存, 最后记录两次的使用差
从test1的收集可看到, collectgarbage(“collect”)被调用,并不保证一次成功, 所以, 大可以调用多次
总结二: 如何避免Lua应用中出现的内存使用过大行为:
当然是代码实现不出现泄露, ,避免使用全局变量,因为存在全局变量可能导致多线程不安全。在创建多个lua虚拟机的时候会2个线程同时操作一个变量。这时候就会出现问题
在测试中,其实还发现, Lua中被分配的内存,其实并不会自动回收(个人估计要么就是Lua虚拟机没有做这个事情,要么就是回收的时机是在C层), 所以, 为了避免内存过大, 应用的运行时,可能需要定期的(调用collectgarbage(“collect”),又或者collectgarbage(“step”))进行显式回收。
借鉴链接:http://blog.csdn.net/henren555/article/details/17579153