Linux | c&cpp | Email | github | QQ群:425043908 关注本站

itarticle.cc

您现在的位置是:网站首页 -> 后端开发 文章内容

检测Lua中临时对象造成的内存增长-itarticl.cc-IT技术类文章记录&分享

发布时间: 8年前后端开发 128人已围观返回

做这事的起因是做服务器压测时发现lua内存每秒涨10M,涨到很高的时候才能被gc降下来,得想办法解决了。之前的《Unity3D游戏优化之Lua的内存》讲过如何查找gc不掉的内存,和windows上用umdh查分配栈找到slua里vector3类型转换造成的临时内存增长。这次说一下通用性查找可被gc掉的内存的方法,比较两个时间的堆快照的对此无效,因为临时对象在俩堆里都没引用。

创建临时对象,比如临时表、临时闭包,向table里加key value,都会使lua虚拟机内存增加,虽然可被gc掉但会造成更频繁的gc,gc速度没分配速度快的话进程容易挂。问题就是如何自动化的找到分配临时对象内存的lua代码,造成内存增长的情况很多怎么找呢,在lua c代码里realloc的时候打印当前代码行?想了2天之后发现方法很简单,50行代码就搞定了,多跟别人扯扯淡还是很有用的,活跃自己的思路,你可以当作智力题想想再往下看。

lua有个hook机制,详见debug.sethook() or Programming in Lua : 23.2,lua profiler就是hook到function call & return事件做计时的,为了降低测量对时间精度的影响实现在了c里。hook机制里还有一个是line事件,每要执行新的一行都会回调,可以用来写lua debugger。上面的内存问题可以转化为找所有使lua虚拟机内存增长的代码行,每次line事件回调时比较内存是否增长了,涨了就记下代码行,累加次数和内存量,一段时间之后排序输出报告即可。黑白代码如下,您稍微改改就能运行

-- 每一行代码的内存增长次数、大小k

local memStat = { }

local currentMem = 0

-- 是否按行统计,否则只按文件统计

local statLine = true

local function RecordAlloc(event, lineNo)

local memInc = collectgarbage("count") - currentMem

-- 没涨内存就不统计

if (memInc <= 1e-6) then return end

-- 2nd from stack top is the func hooked

local s = debug.getinfo(2, 'S').source

if statLine then

s = string.format("%s__%d", s, lineNo - 1)

end

local item = memStat[s]

if (not item) then

memStat[s] = { s, 1, memInc }

else

item[2] = item[2] + 1

item[3] = item[3] + memInc

end

-- 最后再读一次内存,忽略本次统计引起的增长

currentMem = collectgarbage("count")

end

function SC_MemLeakDetector.SC_StartRecordAlloc(igoreLine)

if (debug.gethook()) then

SC_MemLeakDetector.SC_StopRecordAllocAndDumpStat()

return

end

memStat = { }

currentMem = collectgarbage("count")

statLine = not igoreLine

-- hook到每行执行

debug.sethook(RecordAlloc, 'l')

end

function SC_MemLeakDetector.SC_StopRecordAllocAndDumpStat(filename)

debug.sethook()

if (not memStat) then return end

local sorted = { }

for k, v in pairs(memStat) do

table.insert(sorted, v)

end

-- 按内存排序

table.sort(sorted, function(a, b) return a[3] > b[3] end)

filename = filename or "memAlloc.csv"

local file = io.open(filename, "w")

if (not file) then

logw.error("can't open file:", filename)

return

end

file:write("fileLine, count, mem K, avg K\n")

for k, v in ipairs(sorted) do

file:write(string.format("%s, %d, %f, %f\n", v[1], v[2], v[3], v[3] / v[2]))

end

file:close()

memStat = nil

end

立即就能找到热点,具体原因就不好意思说了,反正是创建临时表的问题。

服务器客户端(unity+slua)都能用,对性能有一定影响,帧率能降低几倍

发布时间: 8年前后端开发128人已围观返回回到顶端

很赞哦! (1)

文章评论

  • 请先说点什么
    热门评论
    127人参与,0条评论

站点信息

  • 建站时间:2016-04-01
  • 文章统计:728条
  • 文章评论:82条
  • QQ群二维码:扫描二维码,互相交流