最近突然有了对lua内存泄漏进行检测的需求,去研究了一下lua的说明书,发现lua的hook机制可以很好地满足要求。
1 hook机制
lua的hook属于debug库,可以在你的程序触发了某些事件的时候,调用你所注册的一个函数。
可以触发hook函数的事件有四种:
- call事件: lua调用了一个函数的时候
- return事件:lua的函数返回了
- line事件:当lua开始执行新的一行代码
- count事件:在指定的数字后执行?
lua在调用hook函数的时候,将会带有一个参数,一个字符串来表示生成这次调用的类型,也就是"call", "return", "line", or "count"中的一种。特殊的是,对于line事件,他还会带第二个参数,行号。 当然,我们可以用debug.getinfo来获得hook中的更多信息。
注册一个hook需要使用debug.sethook函数,带两个或者三个参数,第一个是hook的回调函数,也就是事件触发的时候需要调用的函数,第二个参数是一个用来标识我们需要监听的函数类型的字符串,还有一个可选的参数标识调用的频率。
第二个参数,可以是c´,
r´, or `l´来分别代表四个事件。
如果sethook函数没有任何参数,就代表停用hook。
2 内存检测
核心在于
- collectgabage("count")将会返回当前所占用的内存,并进行一次GC
- debug.getinfo() 函数可以在调用的时候获取行号
使用方法:
将代码放到你的文件夹中,在addon_game_mode.lua
或者你的入口处的文件中require对应的文件,之后你可以使用utilsMemoryLeakDetector:ShowRecord(count)函数,或者使用debug_dump_lua_memory_detail
的控制台指令来查看当前的状态
local memory_state = {} -- 储存所有的内存调用
local current_memory = 0 -- 储存总内存状态
local function recordAlloc(event, line_no)
local memory_increased = collectgarbage("count") - current_memory
if (memory_increased < 1e-6) then return end
local info = debug.getinfo(2, "S").source
info = string.format("%s@%s", info, line_no - 1)
local item = memory_state[info]
if not item then
memory_state[info] = {info, 1, memory_increased}
else
item[2] = item[2] + 1
item[3] = item[3] + memory_increased
end
current_memory = collectgarbage("count")
end
utilsMemoryLeakDetector = {}
function utilsMemoryLeakDetector:StartRecord()
if debug.gethook() then
self:StopRecord()
return
end
memory_state = {}
current_memory = collectgarbage("count")
debug.sethook(recordAlloc, "l")
end
function utilsMemoryLeakDetector:ShowRecord(count)
if not memory_state then return end
local sorted = {}
for k, v in pairs(memory_state) do
table.insert(sorted, v)
end
table.sort(sorted, function(a, b) return a[3] > b[3] end)
for i = 1, count do
local v = sorted[i]
print(string.format("MemoryDump [MEM: %sK] [COUNT: %s ] [AVG: %s k] %s:", v[3], v[2], v[3] / v[2], v[1]))
end
end
function utilsMemoryLeakDetector:EndRecord()
debug.sethook()
end
utilsMemoryLeakDetector:StartRecord()
if IsInToolsMode() then
Convars:RegisterCommand("debug_dump_lua_memory_detail",function(_, top_count)
count = tonumber(top_count) or 30
utilsMemoryLeakDetector:ShowRecord(count)
end,"Dump lua usage",FCVAR_CHEAT)
end