local monitor = {} local _tableCount local _functionCount local _userdataCount local collectedObject local objectList local getPath = function(name) local formatName = string.gsub(name, "%.", '/') local index = string.find(formatName, '/') or 0 local path = string.sub(formatName, 1, index - 1) return path end local CollectFromFunction local CollectFromTable local CollectFromUserData local chooseCollect = function(key, obj) local typeStr = type(obj) if typeStr == 'table' then CollectFromTable(key, obj) elseif typeStr == 'function' then CollectFromFunction(key, obj) elseif typeStr == 'userdata' then CollectFromUserData(key, obj) end end CollectFromTable = function(k, t, isRoot) if t == monitor or collectedObject[t] == true then return end -- collectedObject[t] == false 意味着是在已经注册过的根,但是还没有统计到 -- 如果此时的isRoot为false,则先不进行统计知道isRoot为true时说明是从根统计的此对象,此时再进行统计 if collectedObject[t] == false and not isRoot then return end _tableCount = _tableCount + 1 collectedObject[t] = true for i, v in pairs(t) do chooseCollect(i, v) chooseCollect(i, i) end local metaT = getmetatable(t) if metaT then CollectFromTable(k, metaT) end end CollectFromFunction = function(k, f) if collectedObject[f] == true then return end _functionCount = _functionCount + 1 collectedObject[f] = true local i = 1 repeat local upKey, upValue = debug.getupvalue(f, i) if upValue then chooseCollect(upKey, upValue) chooseCollect(upKey, upKey) end i = i + 1 until upKey == nil end CollectFromUserData = function(k, u) if collectedObject[u] == true then return end _userdataCount = _userdataCount + 1 collectedObject[u] = true local metaT = getmetatable(u) if metaT then CollectFromTable(k, metaT) end end local function CollectObjects(prefix, t) for k, v in pairs(t) do _tableCount = 0 _functionCount = 0 _userdataCount = 0 if v ~= t and type(v) == 'table' then CollectFromTable(k, v, true) elseif type(v) == 'function' then CollectFromFunction(k, v) elseif type(v) == 'userdata' then CollectFromUserData(k, v) end if _tableCount ~= 0 or _functionCount ~= 0 or _userdataCount ~= 0 then table.insert(objectList, {prefix, k, _tableCount, _functionCount, _userdataCount}) end end end local _require local _loadedModule --[[ 开启内存检测,在入口处添加此方法 ]] -- function monitor.StartMonitor() collectgarbage("stop") _loadedModule = LuaExt.CreateTable(0, 5000) _require = require local _childTotalMemStack = {} local _modeStack = {} _G.require = function(mod, ...) local v if not _loadedModule[mod] then table.insert(_childTotalMemStack, 0) table.insert(_modeStack, mod) collectgarbage() local preM = collectgarbage("count") v = _require(mod, ...) collectgarbage() local curM = collectgarbage("count") local totalMemory = curM - preM if totalMemory < 0 then -- 可能存在一些误差导致memory为负数 totalMemory = 0 end local selfChildMemory = table.remove(_childTotalMemStack) table.remove(_modeStack) local topIndex = #_childTotalMemStack if topIndex > 0 then _childTotalMemStack[topIndex] = _childTotalMemStack[topIndex] + totalMemory end local stackTrace = "" for i, v in ipairs(_modeStack) do stackTrace = stackTrace .. v .. '--' end stackTrace = stackTrace .. mod _loadedModule[mod] = {curM, totalMemory - selfChildMemory, totalMemory, stackTrace} else v = _require(mod, ...) end return v end end --[[ 统计全部数据并生成表格 ]] -- function monitor.CollectFunction() -- memory collect if _loadedModule then local tableByPath_Mem = {} local memoryTotalBuffer = { "name,curTotalMemory,selfMemory,memory" } local memorySummaryBuffer = { "path,memory" } for k, mod in pairs(_loadedModule) do local totalStr = k .. ',' for i, v in ipairs(mod) do totalStr = totalStr .. v .. ',' end table.insert(memoryTotalBuffer, totalStr) local path = getPath(k) if not tableByPath_Mem[path] then tableByPath_Mem[path] = mod[2] else tableByPath_Mem[path] = tableByPath_Mem[path] + mod[2] end end for k, v in pairs(tableByPath_Mem) do local memStr = k .. ',' .. v table.insert(memorySummaryBuffer, memStr) end LuaExt.FileWriteText("lua_memory_detail.csv", table.concat(memoryTotalBuffer, "\n")) LuaExt.FileWriteText("lua_memory_summary.csv", table.concat(memorySummaryBuffer, "\n")) end -- object collectedObject = {} objectList = {} for i, v in pairs(package.loaded) do collectedObject[v] = false end for i, v in pairs(_G) do collectedObject[v] = false end for i, v in pairs(debug.getregistry()) do collectedObject[v] = false end CollectObjects('package:', package.loaded) CollectObjects('global:', _G) CollectObjects('registry:', debug.getregistry()) local objDetailBuffer = { "name,table,function,userdata" } local objSummaryBuffer = { "path,table,function,userdata" } local tableByPath_Obj = {} for i, v in ipairs(objectList) do local detailStr = v[1] .. v[2] .. ',' .. v[3] .. ',' .. v[4] .. ',' .. v[5] table.insert(objDetailBuffer, detailStr) local path if v[1] == 'registry:' then path = 'registry' elseif v[1] == 'global:' then path = 'global' else path = getPath(v[2]) end if not tableByPath_Obj[path] then tableByPath_Obj[path] = {v[3], v[4], v[5]} else local objByPath = tableByPath_Obj[path] objByPath[1] = objByPath[1] + v[3] objByPath[2] = objByPath[2] + v[4] objByPath[3] = objByPath[3] + v[5] end end for k, v in pairs(tableByPath_Obj) do local summaryStr = k .. ',' .. v[1] .. ',' .. v[2] .. ',' .. v[3] table.insert(objSummaryBuffer, summaryStr) end LuaExt.FileWriteText("lua_object_detail.csv", table.concat(objDetailBuffer, "\n")) LuaExt.FileWriteText("lua_object_summary.csv", table.concat(objSummaryBuffer, "\n")) end return monitor