Lua内存泄露检查

lua中支持垃圾回收机制的对象有五种:string,table,function,full userdata,thread。
他们的引用直接或间接的保存到:lua_state对象,_G全局表,Registry注册表,global_state->mt中。
在脚本中:
- 运行的lua脚本本身就是lua_state。
- _G就是_G全局表。
- Registry表可以用debug.getregistry获取。
- global_mt可以用debug.getmetatable获取。

在搜索时需要注意的几点:

  1. table 额外搜索metatable,若metatable中的__mode取值为”k”、”v”或者”kv”需特殊处理(补充中有说明)
  2. function 额外搜索 enviroment,也是一个table。额外搜索upvalues,这个可以是任何类型。
  3. 由于userdata在script层次不能被修改,所以搜搜他的metatable吧
  4. thread对象就是coroutine对象,在script中一般都不会创建多个coroutine,所以在脚本中没搜索它。若是需求的话,获取到它的线程函数,然后再按照第2步操作就可以了。

云风的泄漏检查工具

工具链接:https://github.com/cloudwu/lua-snapshot
对当前的 Lua State 做一个完整的快照,记录下所有 table thread userdata function 间的引用,在不同时间,对 Lua State 拍两个快照,相比较后,就很容易知道新增加的内存处于何处了。
Lua 代码的运行过程本身会影响 State ,用 C 直接调用 lua API 来遍历 lua State ,影响要小的多。
snapshot 的返回结果只是一张简单的 table ,每个 Lua 对象,都以指针(lightuserdata) 为 key 储存在表中;对应的 value 是一个 string 足够详细的描述了这个对象的引用关系。

ref: http://blog.codingnow.com/2012/12/lua_snapshot.html

使用弱引用表

为了发现内存泄漏,我们可以创建一个全局的弱引用table,使其key为弱引用,然后在每次创建那些可能存在泄漏的对象的时候,都放入这个table,让其作为key,value通常我会用当前时间。由于弱引用的性质,如果其他引用都消失了,那么在弱引用table中对这个对象的引用也会消失(变成nil),反之,只要还有其它任何一个引用存在,这个弱引用表中对这个对象的引用就继续存在。依赖这个特性,当程序已经跑过释放对象的逻辑后,如果这个表中还存在有这个对象的引用,那么这个对象肯定就是泄漏了。
既然内存泄漏一定有引用没清,那么基于lua的特性,这个引用一定存在于_G下面的某个table或者function的upvalue中。

local findedObjMap = nil

function _G.findObject(obj, findDest)
    if findDest == nil then
        return false
    end
    if findedObjMap[findDest] ~= nil then
        return false
    end
    findedObjMap[findDest] = true
    local destType = type(findDest)
    if destType == "table" then
        if findDest == _G.CMemoryDebug then
            return false
        end
        for key, value in pairs(findDest) do
            if key == obj or value == obj then
                _info("Finded Object")
                return true
            end
            if findObject(obj, key) == true then
                _info("table key")
                return true
            end
            if findObject(obj, value) == true then
                _info("key:["..tostring(key).."]")
                return true
            end
        end
    elseif destType == "function" then
        local uvIndex = 1
        while true do
            local name, value = debug.getupvalue(findDest, uvIndex)
            if name == nil then
                break
            end
            if findObject(obj, value) == true then
                _info("upvalue name:["..tostring(name).."]")
                return true
            end
            uvIndex = uvIndex + 1
        end
    end
    return false
end

function _G.findObjectInGlobal(obj)
    findedObjMap = {}
    setmetatable(findedObjMap, {__mode = "k"}) --设置key弱引用
    _G.findObject(obj, _G)
end

ref: https://blog.csdn.net/xocoder/article/details/42685685

待补充

Unity Tolua集成工具 https://blog.csdn.net/tj134679258/article/details/71087630
Lua实现,结果打印txt https://blog.csdn.net/damenhanter/article/details/71380666

猜你喜欢

转载自blog.csdn.net/crayon_chen/article/details/80325492