Lua内存分析工具
最近给公司写了一个lua内存分析工具,可以方便的分析出Lua内存泄露问题(虽然还没正式使用,但我是这样想的,哈哈哈),有图形化界面操作,方便手机端上传快照等功能
内存分析我是在c语言端写的,也有人写过lua端的分析工具,也蛮好用的,不过lua分析工具本身也会影响到lua的内存占用(尽管用的是弱表缓存的),也会有些不准确。
Lua方案:https://github.com/yaukeywang/LuaMemorySnapshotDump
然后找到了云风大神写的C语言解决方案
https://blog.codingnow.com/2012/12/lua_snapshot.html
这个库功能颇为简单,简单到连对象引用链都没有,只打印出key名和内存地址
所以我还是决定自己造轮子改进一下云风大神的方案,也是更进一步的去学习一下lua的c api
C实现起来比Lua复杂一些
- 因为要操作Lua栈,稍微写错一个栈没对称弹出,就会导致溢出,调试起来非常麻烦
- 因为c语言就像一块空地,什么都要自己造,连一些最基本的数据结构,都没有...
- 你需要编译成各个平台的库,这个后面会讲到如何跟tolua c编译到一起
工具分为2个部分
- c库生成快照
- web端接收上传快照,快照分析

Lua中哪些数据类型是需要GC的?
lua源码中定义了这些数据类型
/*
** basic types
*/
#define LUA_TNONE (-1)
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
使用GCObject的联合体将所有需要进行垃圾回收的数据囊括了进来。
/*
** Union of all collectable objects
*/
union GCObject {
GCheader gch;
union TString ts;
union Udata u;
union Closure cl;
struct Table h;
struct Proto p;
struct UpVal uv;
struct lua_State th; /* thread */
};
但是还有一些不需要GC的数据类型,所以又定义了一个Value的联合体
/*
** Union of all Lua values
*/
typedef union {
GCObject *gc;
void *p;
lua_Number n;
int b;
} Value;
这样就可以将Lua中所有的数据类型表示出来了,Lua还使用了一个宏来判断哪些数据类型是需要GC的
#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
通过这个我们可以知道,定义在LUA_TSTRING后的数据类型,都需要GC。一共有:LUA_TSTRING、LUA_TTABLE、LUA_TFUNCTION、LUA_TUSERDATA、LUA_TTHREAD
通过这样的遍历方式,从根节点开始递归整颗GC树

如何遍历table?
void mark_object(lua_State *L, const char *desc, struct lua_gc_node *parent)
{
luaL_checkstack(L, LUA_MINSTACK, NULL);
int t = lua_type(L, -1);
switch (t) {
case LUA_TTABLE:
mark_table(L, desc, parent);
break;
case LUA_TUSERDATA:
mark_userdata(L, desc, parent);
break;
case LUA_TFUNCTION:
mark_function(L, desc, parent);
break;
case LUA_TTHREAD:
mark_thread(L, desc, parent);
break;
default:
lua_pop(L,1);
break;
}
}
void mark_table(lua_State *L, const char *desc, struct lua_gc_node *parent)
{
const void *p = lua_topointer(L, -1);
if(p == NULL)
{
return;
}
if(isMark(p))
{
lua_pop(L, 1);
return;
}
struct lua_gc_node *currNode = gen_node(L, p, desc, parent);
bool weakk = false;
bool weakv = false;
if(lua_getmetatable(L, -1))
{
lua_pushliteral(L, "__mode");
lua_rawget(L, -2);
if (lua_isstring(L,-1))
{
const char *mode = lua_tostring(L, -1);
if (strchr(mode, 'k'))
{
weakk = true;
}
if (strchr(mode, 'v'))
{
weakv = true;
}
}
lua_pop(L,1);
luaL_checkstack(L, LUA_MINSTACK, NULL);
mark_table(L, ".[metatable]", currNode);
}
lua_pushnil(L);
while (lua_next(L, -2) != 0)
{
if(weakv)
{
lua_pop(L, 1);
}
else
{
char temp[128];
const char * _key = keystring(L, -2, temp);
mark_object(L, _key, currNode);
}
if(!weakk)
{
lua_pushvalue(L,-1);
mark_object(L, ".[key]", currNode);
}
}
lua_pop(L, 1);
}
const void *p = lua_topointer(L, -1);
取出栈顶的指针,下面用到指针做key存入一个哈希表里,来标记是否被遍历过
从metatable中取出__mode,来判断key,value是否为弱引用。如果是弱引用就不需要继续递归了,否则就继续调用mark_object递归
通过lua_next方法可以取出table中的key,value压入栈中
这里一定要严谨使用lua_pop(L, 1)管理虚拟栈的平衡,否则栈很快就溢出了
其他的函数可以多查找Lua手册,里面说的很详细,我就不一一列举啦。
另外在c语言中自己创建的内存,需要手动释放,否则也会有内存溢出问题
s = malloc(sizeof(struct lua_gc_node)); 通过malloc开辟内存
free(current_node); 对应使用free释放
递归完毕后,输出成Json格式的快照文件,方便Web端操作。
Web端功能
- 手机上文件传到PC上不太方便,所以弄了个web端直接接收上传的快照文件
- 取补集(可以取出2个快照之间,新创建了哪些东西没释放掉),比如战斗前快照,跟战斗后快照进行取补集,就可以知道战斗内有哪些是没释放的,立马就能查出泄露
- 取交集(可以查询常住内存)
上传文件的php代码
<?php
if ($_FILES["file"]["error"] > 0)
{
echo "错误:" . $_FILES["file"]["error"] . "<br>";
}
else
{
if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " 文件已经存在。 ";
}
else
{
// 如果 upload 目录不存在该文件则将文件上传到 upload 目录下
move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]);
echo "文件存储在: " . "upload/" . $_FILES["file"]["name"];
}
}
?>
如何把我们的代码编译到toluac中?
在网上搜这方面的资料,找到了之前同事(外号姐夫)写的博客,哈哈
https://www.jianshu.com/p/5a35602adef8
他讲的很清楚,我这里就不写了,可以看这篇文章把环境搭好。
另外还需要在tolua#中的LuaDLL.cs类里加上一个方法引入我们的库函数

然后在LuaManager.cs中把函数注册进去给lua使用
lua.OpenLibs (LuaDLL.luaopen_snapshot37);
lua.LuaSetField(-2, "snapshot37");
这样在lua代码里,我们就可以通过
local snapLib = require "snapshot37"
来引入我们的函数了
总结
- Lua内存分析工具的一些解决方案
- Lua中各种数据类型是怎么表示的
- 遍历GCObject的步骤
- 具体的一些LUA C API有不明白的可以多查看Lua官方文档
本文主要介绍了以上内容,欢迎找我一起交流讨论
参考
https://blog.codingnow.com/2012/12/lua_snapshot.html
http://www.cnblogs.com/yaukey/p/unity_lua_memory_leak_trace.html
https://www.jianshu.com/p/5a35602adef8
https://troydhanson.github.io/uthash
《Lua设计与实现》—— codedump (作者)
Lua内存分析工具的更多相关文章
- [原创]推荐一款强大的.NET程序内存分析工具.NET Memory Profiler
[原创]推荐一款强大的.NET程序内存分析工具.NET Memory Profiler 1 官方网站:http://memprofiler.com/2 下载地址:http://memprofiler. ...
- JS内存泄漏 和Chrome 内存分析工具简介(摘)
原文地址:http://web.jobbole.com/88463/ JavaScript 中 4 种常见的内存泄露陷阱 原文:Sebastián Peyrott 译文:伯乐在线专栏作者 - AR ...
- Android 内存分析工具 MAT(Memory Analyzer Tool)
如果使用DDMS确实发现了我们的程序中存在内存泄漏,那又如何定位到具体出现问题的代码片段,最终找到问题所在呢?如果从头到尾的分析代码逻辑,那肯定 会把人逼疯,特别是在维护别人写的代码的时候.这里介绍一 ...
- Android内存分析工具DDMS heap + MAT 安装和使用
一 Java内存分析工具扫盲 如果像我一样一点都不了解,可以先进行内存分析工具扫盲 MAT介绍: Eclipse Memory Analyzer(MAT)一个功能丰富的 JAVA 堆转储 ...
- Android内存优化(三)详解内存分析工具MAT
前言 在这个系列的前四篇文章中,我分别介绍了DVM.ART.内存泄漏和内存检测工具的相关知识点,这一篇我们通过一个小例子,来学习如何使用内存分析工具MAT. 1.概述 在进行内存分析时,我们可以使用M ...
- Eclipse MAT内存分析工具(Memory Analyzer Tool)
MAT内存分析工具 MAT是Memory Analyzer的简称,它是一款功能强大的Java堆内存分析器.可以用于查找内存泄露以及查看内存消耗情况.MAT是基于Eclipse开发的,是一款免费的性能分 ...
- [转] python运行时内存分析工具meliae
转自:https://my.oschina.net/markco/blog/601773 利用meliae来监控python进程的内存占用情况 meliae是一个python进程内存占用监控.分析工具 ...
- 内存分析工具-MAT(Memory Analyzer Tool)
内存分析工具-MAT(Memory Analyzer Tool) 首先查看如下代码,main函数中有一个成员变量map,map里被循环放入对象Hanson,hanson持有姓名和age还有friend ...
- android--------Eclipse中ddms heap内存分析工具
无 论怎么小心,想完全避免bad code是不可能的,此时就需要一些工具来帮助我们检查代码中是否存在会造成内存泄漏的地方. Android tools中的DDMS就带有一个很不错的内存监测工具Heap ...
随机推荐
- 给织梦DEDECMS添加栏目图片与英文名显示
开始做微网站了,不同于传统手机网站,因为微信上的微网站是支持CSS3与HTML5的,好吧,各种要学习的还有很多很多阿~这么多新代码,叹! 本来想转战帝国CMS了,奈何这名字太不对味了,PHPCMS也懒 ...
- 在MAC电脑上抓取iphone数据包的方法
一.说明: 1.整个抓包操作的过程中,手机必须一直通过USB链接MAC电脑 2.手机系统要求在IOS5以上,因为使用的RVI技术在IOS5以后的系统中才有 3.抓包过程中,手机可以使用任何网络2G.3 ...
- SDP(3):ScalikeJDBC- JDBC-Engine:Fetching
ScalikeJDBC在覆盖JDBC基本功能上是比较完整的,而且实现这些功能的方式比较简洁,运算效率方面自然会稍高一筹了.理论上用ScalikeJDBC作为一种JDBC-Engine还是比较理想的:让 ...
- MySql Outer Join 简单化
查询from语句中的Outer Join可以在多种情况下被简化: 在解析阶段,右外连接操作可以被转变为等下ode值包含left join的操作,在一般情况下,转变: (T1, ...) RIGHT J ...
- python文件操作总结
python中对文件.文件夹(文件操作函数)的操作需要涉及到os模块和shutil模块. 得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd() 返回指定目录下的所有文件和目 ...
- 反应堆模式(reactor)
在提到高性能服务器编程的时候肯定有听过reactor模式,如果只是简单的写一个服务器和客户端建立连接的程序来熟悉一下使用socket函数编程,一般这种情况都是同步方式实现的,服务器阻塞等待客户端的连接 ...
- java里程碑之泛型--泛型注意的几点
1,泛型的基本语法:类名<具体类> 对象名 = new 类名<具体类>().类型参数规范如下: 1),K键,比如映射的键,key的类型 2),V值,比如Map的值,value类 ...
- 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
/** * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名. */ private function createSign($parameters,$key) { $signP ...
- 使用jQuery的ajax调用action的例子
直接使用ajax请求会比较繁琐,但是jQuery为我们提供了简单使用ajax的方法. 下面是一个在jQuery easyUI中,利用ajax请求,使下拉菜单关联文本框的例子.其中ajax请求就是8-1 ...
- web.xml 中<context-param>与<init-param>的区别与作用
<context-param>的作用: web.xml的配置中<context-param>配置作用 1. 启动一个WEB项目的时候,容器(如:Tomcat)会去读它的配置文件 ...
