├── README.MD └── memleak.lua /README.MD: -------------------------------------------------------------------------------- 1 | ## API: 2 | ``` 3 | MemLeak:init(file) -- init MemLeak by out file 4 | MemLeak:cacheMemory() -- cache current memory state 5 | MemLeak:showCache(index) -- show memory state 6 | MemLeak:differCache() -- diff memory state 7 | 8 | MemLeak:fixTableDesc(object) -- fix this function, add your description 9 | MemLeak:fixUserdataDesc(object, desc) -- fix this function, add your description 10 | ``` 11 | 12 | ## SAMPLE: 13 | ``` 14 | ------------------------------------------------ 15 | -- test 16 | MemLeak:init([[memleak.log]]) -- init MemLeak op 17 | MemLeak:cacheMemory() -- cache memory, index == 1 18 | 19 | local aaaaaaaaa = {} 20 | bbbbbbbbbb = {} 21 | 22 | MemLeak:cacheMemory() -- cache memory, index == 2 23 | MemLeak:differCache() -- diff memory cache 24 | --------------------------------------------------------- 25 | ``` 26 | 27 | ## 中文说明: 28 | http://radiotail.github.io/2014/11/03/%E6%9F%A5%E6%89%BElua%E5%B1%82%E7%9A%84%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F%E9%97%AE%E9%A2%98/ 29 | -------------------------------------------------------------------------------- /memleak.lua: -------------------------------------------------------------------------------- 1 | local pairs = pairs 2 | local print = print 3 | local type = type 4 | local select = select 5 | local tostring = tostring 6 | local stringFormat = string.format 7 | 8 | local MemLeak = { 9 | memCache1 = nil, 10 | memCache2 = nil, 11 | nowCache = nil, 12 | relation = {}, 13 | parentsValue = {}, 14 | output = io.stdout, 15 | } 16 | 17 | function MemLeak:cacheMemory() 18 | if self.memCache1 and self.memCache2 then 19 | self:prints("You had two caches, please differ them!") 20 | return 21 | end 22 | 23 | local markedMap = {} 24 | markedMap.markedTable = {} 25 | markedMap.markedFunction = {} 26 | markedMap.markedUserdata = {} 27 | 28 | local stage = 0 29 | if not self.memCache1 then 30 | self.memCache1 = markedMap 31 | stage = 1 32 | elseif not self.memCache2 then 33 | self.memCache2 = markedMap 34 | stage = 2 35 | end 36 | 37 | self.nowCache = markedMap 38 | 39 | self:searchRegistry() 40 | self:searchGlobe() 41 | 42 | self:prints("cache stage: ", stage, " time: ", os.time()) 43 | print("cacheMemory over!") 44 | end 45 | 46 | function MemLeak:getCacheByIndex(index) 47 | if index == 1 then 48 | return self.memCache1 49 | elseif index == 2 then 50 | return self.memCache2 51 | end 52 | end 53 | 54 | function MemLeak:clearCache() 55 | self.memCache1 = nil 56 | self.memCache2 = nil 57 | self.nowCache = nil 58 | self.relation = {} 59 | self.parentsValue = {} 60 | end 61 | 62 | function MemLeak:markedCount(index) 63 | local marked = self:getCacheByIndex(index) 64 | if marked then 65 | self:prints("error index!", index) 66 | return 67 | end 68 | 69 | local count = 0 70 | 71 | local tb1 = marked.markedTable 72 | for k, v in pairs(tb1) do 73 | count = count + 1 74 | end 75 | 76 | tb1 = marked.markedFunction 77 | for k, v in pairs(tb1) do 78 | count = count + 1 79 | end 80 | 81 | tb1 = marked.markedFunction 82 | for k, v in pairs(tb1) do 83 | count = count + 1 84 | end 85 | 86 | self:prints("object count: ", count) 87 | end 88 | 89 | function MemLeak:differCache() 90 | local marked1 = self:getCacheByIndex(1) 91 | if not marked1 then 92 | self:prints("you don't have cache1") 93 | return 94 | end 95 | 96 | local marked2 = self:getCacheByIndex(2) 97 | if not marked2 then 98 | self:prints("you don't have cache2") 99 | return 100 | end 101 | 102 | self:prints("\nnew objects list: ") 103 | local count = 0 104 | local differs = {} 105 | local tb1, tb2 = marked1.markedTable, marked2.markedTable 106 | local func1, func2 = marked1.markedFunction, marked2.markedFunction 107 | local user1, user2 = marked1.markedUserdata, marked2.markedUserdata 108 | 109 | for k, v in pairs(tb2) do 110 | if not tb1[k] then 111 | self:printResult(k, v) 112 | self:findDiffersParents(differs, tb1, func1, user1, k, v) 113 | count = count + 1 114 | end 115 | end 116 | 117 | for k, v in pairs(func2) do 118 | if not func1[k] then 119 | self:printResult(k, v) 120 | self:findDiffersParents(differs, tb1, func1, user1, k, v) 121 | count = count + 1 122 | end 123 | end 124 | 125 | for k, v in pairs(user2) do 126 | if not user1[k] then 127 | self:printResult(k, v) 128 | self:findDiffersParents(differs, tb1, func1, user1, k, v) 129 | count = count + 1 130 | end 131 | end 132 | 133 | self:prints("object count: ", count) 134 | self:prints("\n") 135 | 136 | self:prints("parents list: ") 137 | for key, value in pairs(differs) do 138 | self:printResult(key, value) 139 | end 140 | self:prints("\n") 141 | 142 | self:printRelation() 143 | self:flushOutput() 144 | self:clearCache() 145 | 146 | print("differCache over!") 147 | end 148 | 149 | function MemLeak:findDiffersParents(differs, tb, func, user, key, value) 150 | local parents = value[3] 151 | local parentKey, parentValue 152 | for i = 1, #parents do 153 | parentKey = parents[i] 154 | parentValue = tb[parentKey] 155 | if not differs[parentKey] then 156 | if not parentValue then 157 | parentValue = func[parentKey] 158 | end 159 | 160 | if not parentValue then 161 | parentValue = user[parentKey] 162 | end 163 | 164 | if parentValue then 165 | differs[parentKey] = parentValue 166 | self:findDiffersParents(differs, tb, func, user, parentKey, parentValue) 167 | end 168 | end 169 | end 170 | end 171 | 172 | function MemLeak:showCache(index) 173 | local marked = self:getCacheByIndex(index) 174 | if not marked then 175 | self:prints("error index!", index) 176 | return 177 | end 178 | 179 | self:prints("show cache index: ", index) 180 | local count = 0 181 | for k, v in pairs(marked.markedTable) do 182 | self:printResult(k, v) 183 | count = count + 1 184 | end 185 | 186 | for k, v in pairs(marked.markedFunction) do 187 | self:printResult(k, v) 188 | count = count + 1 189 | end 190 | 191 | for k, v in pairs(marked.markedUserdata) do 192 | self:printResult(k, v) 193 | count = count + 1 194 | end 195 | 196 | self:prints("object count: ", count) 197 | end 198 | 199 | function MemLeak:filter(object, varType, parent, desc) 200 | if object == self or 201 | object == self.nowCache or 202 | object == self.relation or 203 | object == self.parentsValue or 204 | object == self.memCache1 or 205 | object == self.memCache2 then 206 | 207 | return true 208 | end 209 | end 210 | 211 | function MemLeak:isMarked(object, varType, parent, desc) 212 | local marked 213 | local markedMap = self.nowCache 214 | if varType == "table" then 215 | if self:filter(object, varType, parent, desc) then 216 | return true 217 | end 218 | marked = markedMap.markedTable 219 | elseif varType == "function" then 220 | marked = markedMap.markedFunction 221 | elseif varType == "userdata" then 222 | marked = markedMap.markedUserdata 223 | end 224 | 225 | local keyString = tostring(object) 226 | local parentString = tostring(parent) 227 | local tb = marked[keyString] 228 | if not tb then 229 | local parents = {parentString} 230 | marked[keyString] = {1, varType, parents, desc} 231 | else 232 | tb[1] = tb[1] + 1 233 | local parents = tb[3] 234 | parents[#parents + 1] = parentString 235 | return true 236 | end 237 | 238 | return false 239 | end 240 | 241 | function MemLeak:searchObject(object, parent, desc) 242 | local varType = type(object) 243 | 244 | if varType == "table" then 245 | self:searchTable(object, parent, desc) 246 | elseif varType == "function" then 247 | self:searchFunction(object, parent, desc) 248 | elseif varType == "userdata" then 249 | self:searchUserdata(object, parent, desc) 250 | end 251 | end 252 | 253 | function MemLeak:fixTableDesc(object) 254 | local fixdesc = "" 255 | -- add your custom 256 | return fixdesc 257 | end 258 | 259 | function MemLeak:searchTable(object, parent, desc) 260 | local fixdesc = self:fixTableDesc(object) 261 | desc = desc..fixdesc 262 | 263 | if self:isMarked(object, "table", parent, desc) then return end 264 | 265 | local meta = debug.getmetatable(object) 266 | if meta then 267 | self:searchObject(meta, object, "[metatable]") 268 | end 269 | 270 | local keytype 271 | for key, value in pairs(object) do 272 | keytype = type(key) 273 | if keytype == "string" then 274 | desc = key 275 | elseif keytype == "number" then 276 | desc = tostring(key) 277 | else 278 | self:searchObject(key, object, "[key]") 279 | desc = "[value]" 280 | end 281 | self:searchObject(value, object, desc) 282 | end 283 | end 284 | 285 | function MemLeak:fixFunctionDesc(func) 286 | local info = debug.getinfo(func) 287 | local fixdesc = stringFormat(":[%s:%d]", info.short_src, info.linedefined) 288 | return fixdesc 289 | end 290 | 291 | function MemLeak:searchFunction(func, parent, desc) 292 | local fixdesc = self:fixFunctionDesc(func) 293 | desc = desc..fixdesc 294 | if self:isMarked(func, "function", parent, desc) then return end 295 | 296 | local i = 1 297 | while true do 298 | local name, value = debug.getupvalue(func, i) 299 | if not name then break end 300 | if name == "" then 301 | name = "[upvalue]" 302 | end 303 | 304 | self:searchObject(value, func, name) 305 | i = i + 1 306 | end 307 | end 308 | 309 | function MemLeak:fixUserdataDesc(object, desc) 310 | local fixdesc = "" 311 | -- add your custom 312 | return fixdesc 313 | end 314 | 315 | function MemLeak:searchUserdata(object, parent, desc) 316 | local fixdesc = self:fixUserdataDesc(object, desc) 317 | desc = desc..fixdesc 318 | 319 | if self:isMarked(object, "userdata", parent, desc) then return end 320 | 321 | local meta = debug.getmetatable(object) 322 | if meta then 323 | self:searchObject(meta, object, "[metatable]") 324 | end 325 | end 326 | 327 | function MemLeak:memCount() 328 | self:prints(collectgarbage("count")) 329 | end 330 | 331 | function MemLeak:memCollect() 332 | self:prints("before collect: ", collectgarbage("count")) 333 | self:prints(collectgarbage("collect")) 334 | self:prints("after collect: ", collectgarbage("count")) 335 | end 336 | 337 | function MemLeak:searchGlobe() 338 | self:searchTable(_G, 0, "[globe]") 339 | end 340 | 341 | function MemLeak:searchRegistry() 342 | local registry = debug.getregistry() 343 | self:searchTable(registry, 0, "[registry]") 344 | end 345 | 346 | function MemLeak:prints(...) 347 | local outStr = "" 348 | for i = 1, select('#', ...) do 349 | local temp = select(i, ...) or "" 350 | outStr = outStr..tostring(temp).."\t" 351 | end 352 | outStr = outStr.."\n" 353 | self.output:write(outStr) 354 | end 355 | 356 | function MemLeak:setOutput(output) 357 | if not output then 358 | self:prints("error output!") 359 | return 360 | end 361 | self.output = output 362 | end 363 | 364 | function MemLeak:closeOutput() 365 | self.output:close() 366 | end 367 | 368 | function MemLeak:flushOutput() 369 | self.output:flush() 370 | end 371 | 372 | function MemLeak:printResult(key, value) 373 | local ref, varType, parents, desc = value[1], value[2], value[3], value[4] 374 | local str2 = "" 375 | for i = 1, #parents do 376 | self:intoRelation(key, parents[i]) 377 | str2 = parents[i].."\t"..str2 378 | end 379 | local str1 = stringFormat("object:%s\tdesc:%s\ttype:%s\tref:%s\tparent:%s", key, desc, varType, ref, str2) 380 | self:prints(str1) 381 | end 382 | 383 | function MemLeak:intoRelation(object, parent) 384 | local parentsValue = self.parentsValue 385 | 386 | local value = parentsValue[parent] 387 | if not value then 388 | parentsValue[parent] = 1 389 | else 390 | parentsValue[parent] = value + 1 391 | end 392 | end 393 | 394 | function MemLeak:printRelation() 395 | local parentsValue = self.parentsValue 396 | self:prints("value count list:") 397 | for k, v in pairs(parentsValue) do 398 | self:prints("parent:"..k, "value count:", v) 399 | end 400 | self:prints("\n") 401 | end 402 | 403 | function MemLeak:createOutfile(filename) 404 | local file = assert(io.open(filename, "w")) 405 | self:setOutput(file) 406 | end 407 | 408 | function MemLeak:init(filename) 409 | self:createOutfile(filename) 410 | end 411 | 412 | return MemLeak 413 | ------------------------------------------------ 414 | -- test 415 | -- MemLeak:init([[memleak.log]]) -- init MemLeak op 416 | -- MemLeak:cacheMemory() -- cache memory1 417 | 418 | -- local aaaaaaaaa = {} 419 | -- bbbbbbbbbb = {} 420 | 421 | -- MemLeak:cacheMemory() -- cache memory2 422 | -- MemLeak:differCache() -- differ memory cache 423 | --------------------------------------------------------- 424 | --------------------------------------------------------------------------------