标签:
首先第一点,lua中的内存泄露和我们所说的c/c++中的内存泄露本质上是不一样的。
lua中有垃圾回收机制(GC),所以理论上是不会有内存泄露的。当它进行GC的时候,会从根部开始扫描所有的对象,如果某个地方对这个对象还有引用,就不会把这个对象内存collect,这个对象就没有被GC。所以lua中的内存泄露是指那些:已经没有被使用了,但外部依然还有引用存在的对象。
--函数中应该被申明为local的对象忘记加local local function test() testTable = {} --这个testTabel会被存放在全局表_G中,GC时由于此对象还有引用存在,所以这里总是会有一个table泄露。 local mt = {} --mt加了local修饰,函数调用完后,引用也不复存在了,GC时会被回收。 setmetatable(testTable, mt) end
lua中支持垃圾回收机制的对象有五种:string,table,function,full userdata,thread。而他们的引用直接或间接的保存到:lua_state对象,_G全局表,Registry注册表,global_state->mt中。
在脚本中:
所以我们就可以在脚本层次实现内存泄露的检测模块。
在搜索时需要注意的几点:
在检测泄露之前,先搜索一下所有的对象,保存好起始的内存状态,在程序执行之后执行几次GC操作,然后再进行一次搜索,对比两次的结果,多出来的那些就有可能是内存泄露了。
补充:
lua中有一种叫weak表的东东,它的metatable中的__mode被设置为“k","v"或者”kv",表示保存在它中的键或值或键值都是一种弱引用状态。
若一个对象的所有引用都是弱引用了,那么这个对象也会被GC回收掉,所以对应的weak表中此对象的入口就没有了。
所以我们可以用另外一种实现:就是把用户自己创建的资源对象统统都丢到weak表中,运行完程序后强制GC,然后去查看weak表,若表中还保存着那个对象,就意味着这个对象还有外部引用(相对弱引用我们就叫它为强引用吧),资源没有被GC掉,所以我们可以说这个对象很有可能是内存泄露了。
Lua的GC算法使用的所谓“Mark And Sweep”算法。简单的理解,这个算法将GC分为两个阶段,一个是标记(mark)阶段,这一阶段将所有系统中引用的对象都逐一标记;而在清理(sweep)阶段,将把在mark阶段中没有被标记的数据删除。
在Lua中,使用几种颜色来区分不同的结点:
collectgarbage函数提供了多项功能:停止垃圾回收,重启垃圾回收,强制执行一次回收循环,强制执行一步垃圾回收,获取Lua占用的内存,以及两个影响垃圾回收频率和步幅的参数。collectgarbage(opt,[,arg])
"stop" |
停止垃圾收集器,如果它的运行。 |
"restart" |
如果垃圾收集器已经停止,将重新启动它。 |
"collect" |
执行一次全垃圾收集循环。默认执行此操作 |
"count" |
返回当前Lua中使用的内存量(以KB为单位) |
"step" |
单步执行一个垃圾收集. 步长 "Size" 由参数arg指定 (大型的值需要多步才能完成),如果要准确指定步长,需要多次实验以达最优效果。如果步长完成一次收集循环,将返回True |
"setpause" |
设置 arg/100 的值作为暂定收集的时长;并返回设置前的值。默认为200 控制了收集器在开始一个新的收集周期之前要等待多久。 随着数字的增大就导致收集器工作工作的不那么主动。 小于 1 的值意味着收集器在新的周期开始时不再等待。 当值为 2 的时候意味着在总使用内存数量达到原来的两倍时再开启新的周期。 |
"setstepmul" |
设置 arg/100 的值,作为步长的增幅(即新步长=旧步长*arg/100);并返回设置前的值。默认为200 控制了收集器的工作速度,这个速度是一个相对于内存分配的速度。更大的数字将导致收集器工作的更主动的同时,也使每步收集的尺寸增加。 小于 1 的值会使收集器工作的非常慢,可能导致收集器永远都结束不了当前周期。 缺省值为200%,这意味着收集器将以内存分配器的两倍速运行。 |
function test1() collectgarbage("collect")--为了有干净的环境,先把可以收集的垃圾收集了 collectgarbage()--为了保证内存的收集的相对干净,及内存的稳定,要执行多次收集 print("now,Lua内存为:",collectgarbage("count")) -->205.7158203125 KB local colen = {} --现在是局部变量 for i=1,5000 do table.insert(colen,{}) end print("now,Lua内存为:",collectgarbage("count"))-->860.4111328125 KB --创建5000个table,内存增加了655 KB end function collect1() print("now,Lua内存为:",collectgarbage("count"))-->608.060546875 KB collectgarbage() collectgarbage() print("now,Lua内存为:",collectgarbage("count"))-->204.8408203125 KB --最后与一开始只差只有1KB end
function test2() collectgarbage("collect")--为了有干净的环境,先把可以收集的垃圾收集了 collectgarbage()--为了保证内存的收集的相对干净,及内存的稳定,要执行多次收集 print("now,Lua内存为:",collectgarbage("count")) -->205.7158203125 KB colen = {} --现在是全部变量 for i=1,5000 do table.insert(colen,{}) end print("now,Lua内存为:",collectgarbage("count"))-->619.826171875 KB --创建5000个table,内存增加了414 KB;这些增加的内存,由于已放到了全局函数中,是永远没有机会被回收到了! end function collect2() print("now,Lua内存为:",collectgarbage("count"))-->596.7822265625 KB collectgarbage() collectgarbage() collectgarbage() print("now,Lua内存为:",collectgarbage("count"))-->489.189453125 KB --最后内存增加了284KB(489-205) end
垃圾回收器有两个参数用于控制它的节奏:
第一个参数,称为暂停时间,控制回收器在完成一次回收之后和开始下次回收之前要等待多久;
第二个参数,称为步进系数,控制回收器每个步进回收多少内容。粗略地来说,暂停时间越小、步进系数越大,垃圾回收越快。这些参数对于程序的总体性能的影响难以预测,更快的垃圾回收器显然会浪费更多的CPU周期,但是它会降低程序的内存消耗总量,并可能因此减少分页。只有谨慎地测试才能给你最佳的参数值。
标签:
原文地址:http://blog.csdn.net/shimazhuge/article/details/43794347