[Lua]Lua内存泄露检测原理

lua内存泄露

首先第一点,lua中的内存泄露和我们所说的c/c++中的内存泄露本质上是不一样的。

lua中有垃圾回收机制(GC),所以理论上是不会有内存泄露的。当它进行GC的时候,会从根部开始扫描所有的对象,如果某个地方对这个对象还有引用,就不会把这个对象内存collect,这个对象就没有被GC所以lua中的内存泄露是指那些:已经没有被使用了,但外部依然还有引用存在的对象。

--函数中应该被申明为local的对象忘记加local
local function test()
    testTable = {} --这个testTabel会被存放在全局表_G中,GC时由于此对象还有引用存在,所以这里总是会有一个table泄露。
    local mt = {} --mt加了local修饰,函数调用完后,引用也不复存在了,GC时会被回收。
    setmetatable(testTable, mt)
end

检测原理

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步操作就可以了。

搜索流程图(_G表)

在检测泄露之前,先搜索一下所有的对象,保存好起始的内存状态,在程序执行之后执行几次GC操作,然后再进行一次搜索,对比两次的结果,多出来的那些就有可能是内存泄露了。

补充:

lua中有一种叫weak表的东东,它的metatable中的__mode被设置为“k","v"或者”kv",表示保存在它中的键或值或键值都是一种弱引用状态。

若一个对象的所有引用都是弱引用了,那么这个对象也会被GC回收掉,所以对应的weak表中此对象的入口就没有了。

所以我们可以用另外一种实现:就是把用户自己创建的资源对象统统都丢到weak表中,运行完程序后强制GC,然后去查看weak表,若表中还保存着那个对象,就意味着这个对象还有外部引用(相对弱引用我们就叫它为强引用吧),资源没有被GC掉,所以我们可以说这个对象很有可能是内存泄露了。

Lua垃圾回收算法

Lua的GC算法使用的所谓“Mark And Sweep”算法。简单的理解,这个算法将GC分为两个阶段,一个是标记(mark)阶段,这一阶段将所有系统中引用的对象都逐一标记而在清理(sweep)阶段,将把在mark阶段中没有被标记的数据删除。

在Lua中,使用几种颜色来区分不同的结点:

  • white:白色表示没有进行过标记的节点
  • gray:灰色表示已经进行过标记的节点,但是与它相关联的节点还没有进行过标记。
  • black:本节点和与之关联的节点都已经被扫描标记过了。

通常会出现有关联数据的,包括有Table,upvalue等数据类型。

垃圾收集器函数

collectgarbage函数提供了多项功能:停止垃圾回收重启垃圾回收强制执行一次回收循环强制执行一步垃圾回收获取Lua占用的内存,以及两个影响垃圾回收频率和步幅的参数。collectgarbage(opt,[,arg])


"stop"


停止垃圾收集器,如果它的运行。


"restart"


如果垃圾收集器已经停止,将重新启动它。


"collect"


执行一次全垃圾收集循环。默认执行此操作


"count"


返回当前Lua中使用的内存量(以KB为单位)


"step"


单步执行一个垃圾收集. 步长 "Size" 由参数arg指定 (大型的值需要多步才能完成),如果要准确指定步长,需要多次实验以达最优效果。如果步长完成一次收集循环,将返回True


"setpause"


设置 arg/100 的值作为暂定收集的时长;并返回设置前的值。默认为200

控制了收集器在开始一个新的收集周期之前要等待多久。 随着数字的增大就导致收集器工作工作的不那么主动。 小于 1 的值意味着收集器在新的周期开始时不再等待。 当值为 2 的时候意味着在总使用内存数量达到原来的两倍时再开启新的周期。


"setstepmul"


设置 arg/100 的值,作为步长的增幅(即新步长=旧步长*arg/100);并返回设置前的值。默认为200

控制了收集器的工作速度,这个速度是一个相对于内存分配的速度。更大的数字将导致收集器工作的更主动的同时,也使每步收集的尺寸增加。 小于 1 的值会使收集器工作的非常慢,可能导致收集器永远都结束不了当前周期。 缺省值为200%,这意味着收集器将以内存分配器的两倍速运行。

function test1()
    collectgarbage("collect")--为了有干净的环境,先把可以收集的垃圾收集了
    collectgarbage()--为了保证内存的收集的相对干净,及内存的稳定,要执行多次收集
    print("now,Lua内存为:",collectgarbage("count")) -->205.7158203125 KB
    local colen = {} --现在是局部变量
    for i=1,5000 do
        table.insert(colen,{})
    end
    print("now,Lua内存为:",collectgarbage("count"))-->860.4111328125 KB
    --创建5000个table,内存增加了655 KB
end

function collect1()
    print("now,Lua内存为:",collectgarbage("count"))-->608.060546875 KB
    collectgarbage()
    collectgarbage()
    print("now,Lua内存为:",collectgarbage("count"))-->204.8408203125 KB
    --最后与一开始只差只有1KB
end
function test2()
    collectgarbage("collect")--为了有干净的环境,先把可以收集的垃圾收集了
    collectgarbage()--为了保证内存的收集的相对干净,及内存的稳定,要执行多次收集
    print("now,Lua内存为:",collectgarbage("count")) -->205.7158203125 KB
    colen = {} --现在是全部变量
    for i=1,5000 do
        table.insert(colen,{})
    end
    print("now,Lua内存为:",collectgarbage("count"))-->619.826171875 KB
    --创建5000个table,内存增加了414 KB;这些增加的内存,由于已放到了全局函数中,是永远没有机会被回收到了!
end

function collect2()
    print("now,Lua内存为:",collectgarbage("count"))-->596.7822265625 KB
    collectgarbage()
    collectgarbage()
    collectgarbage()
    print("now,Lua内存为:",collectgarbage("count"))-->489.189453125 KB
    --最后内存增加了284KB(489-205)
end

垃圾回收器有两个参数用于控制它的节奏:

第一个参数,称为暂停时间,控制回收器在完成一次回收之后和开始下次回收之前要等待多久;

第二个参数,称为步进系数,控制回收器每个步进回收多少内容。粗略地来说,暂停时间越小、步进系数越大,垃圾回收越快。这些参数对于程序的总体性能的影响难以预测,更快的垃圾回收器显然会浪费更多的CPU周期,但是它会降低程序的内存消耗总量,并可能因此减少分页。只有谨慎地测试才能给你最佳的参数值。

时间: 2025-01-22 11:23:55

[Lua]Lua内存泄露检测原理的相关文章

自己实现简易的内存泄露检测工具VLD

有一个很著名的内存泄露检测工具Visual leak detected想必大家都不陌生,但今天我们可以自己写一个简易版的.哈哈,自己动手,丰衣足食有木有!!! 它的原理就是我们重载了操作符new和delete,当用new开辟空间的时候,就讲这块空间串到我们定义的结构体MEMNode形成的链表中,(这是老师的写法,在main程序结束时,用check_vld()函数检测有没有内存泄露)但我觉得,如果我们动态开辟了一个对象,在它的析构函数里用delete释放的话,这种方法就不太可行.因为析构函数只有到

【转】c++内存泄露检测,长文慎入!

原文网址:http://blog.csdn.net/zengraoli/article/details/8905334 关于内存泄露的,今天无意想到,网上找了一下 本篇blog附带的所有工具和代码下载地址如下: http://download.csdn.net/detail/zengraoli/5348827 文中的memcheck晚点的时候在把它打包成dll 一.使用Dbgview.exe 不多数都是用的Dbgview.exe,话说还不错,我一直想找的仅仅是一个检测内存泄露的class,没想到

vld(Visual Leak Detector) 内存泄露检测工具

初识Visual Leak Detector 灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题.当程序越来越复 杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题.内存泄漏是最常见的内存问题之一.内存泄漏如果不是很严重,在短时间内对程序不会有太大的 影响,这也使得内存泄漏问题有很强的隐蔽性,不容易被发现.然而不管内存泄漏多么轻微,当程序长时间运行时,其破坏力是惊人的,从性能下降到内存耗尽,甚 至会影响到其他程序的正常运行.另外内存问题的一个共同特点是,内存问题本身

Android 源码系列之<十四>从源码的角度深入理解LeakCanary的内存泄露检测机制(下)

转载请注明出处:http://blog.csdn.net/llew2011/article/details/52958567 在上边文章Android 源码系列之<十三>从源码的角度深入理解LeakCanary的内存泄露检测机制(中)由于篇幅原因仅仅向小伙伴们讲述了在Android开发中如何使用LeakCanary来检测应用中出现的内存泄露,并简单的介绍了LeakCanary的相关配置信息.根据上篇文章的介绍我们知道LeakCanary为了不给APP进程造成影响所以新开启了一个进程,在新开启的

体验LeakCanary如何做内存泄露检测

引子 最近江湖流传一内存泄露检测的神奇-LeakCanary,于是笔者按耐不住激动的心情,想试一把.结果伤不起的,这个工程是gradle的,对于使用eclipse惯了的同学来说伤不起--不过笔者将其改造为eclipse工程了,github地址:https://github.com/cheyiliu/leakcanary/tree/leakcannary_eclipse_project 用法 下载该工程,导入eclipse并作为lib工程 在你的测试工程里引入该lib工程 将lib工程的manif

C/C++内存泄露检测

gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4Copyright (C) 2013 Free Software Foundation, Inc.This is free software; see the source for copying conditions. There is NOwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 一.mtrace 1.介绍

Visual C++内存泄露检测—VLD工具使用说明 .

1.VLD工具概述 Visual Leak Detector(VLD)是一款用于Visual C++的免费的内存泄露检测工具.他的特点有:可以得到内存泄漏点的调用堆栈,如果可以的话,还可以得到其所在文件及行号: 可以得到泄露内存的完整数据: 可以设置内存泄露报告的级别:并且是开源免费的. 2.VLD下载 http://www.codeproject.com/tools/visualleakdetector.asp 本文后附有vld1.0的工具包,下载解包后就可使用. 3.VLD安装 方法一: 解

内存泄露检测工具

VS2008的内存泄露检测功能有限,使用也有些局限性.今天因工作时间紧迫,工程繁琐,我最终选择了VLD(Visual Leak Detector).这个工具使用起来十分简单,只需要: (1)下载安装vld.安装过程中可以发现vld安装向导提示关闭VS2008同时还将vld的头文件include目录路径.vld的库文件lib目录设置好了,简直太贴心.vld下载地址:http://vld.codeplex.com/ (2)在待检测的工程中添加头文件#include "vld.h"(我添加在

内存泄露检测工具——LeakCanary

很简单:我们不是创建服务不是为了赚钱:我们赚钱是为了提供更好的服务.我们认为这才是做事的态度. 学习使用Java的同学都应该知道,Java的JVM给我们提供的垃圾回收机制是极为好用的.但是我们也很清楚,垃圾回收机制不是万能的,使用不当很容易造成内存泄露.之前我们也介绍过Java中常用的内存泄露检测工具MAT,目前Java程序最常用的内存分析工具应该是MAT(Memory Analyzer Tool),它是一个Eclipse插件,同时也有单独的RCP客户端. 不熟悉MAT的同学,或者对Java垃圾