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 ...
随机推荐
- 微信公众号tp3.2放进Model无效,几种实例化的方法试过,还是提示无法提供服务
http://www.imooc.com/video/10230 解决方案一: $indexModel = D('Index'); 实测可行 解决方案一: 使用TP3.2的小伙伴需要注意了,在Mod ...
- 【WebApi系列】浅谈HTTP
[01]浅谈HTTP在WebApi开发中的运用 [02]聊聊WebApi体系结构 [03]详解WebApi如何传递参数 [04]详解WebApi测试和PostMan [05]浅谈WebApi Core ...
- ngRx 官方示例分析 - 5. components
组件通过标准的 Input 和 Output 进行操作,并不直接访问 store. /app/components/book-authors.ts import { Component, Input ...
- docker 安装 msyql
**************************************************************************************************** ...
- ntp 时钟同步
注意: 如果你无法和外部网络的时钟同步,请检查UDP端口时候被封.
- Linuxc - 标准输入流、标准输出流、标准错误流
输入流stdin默认是键盘,输出流stdout默认是显示器,错误流stderr #include <stdio.h> int main() { printf("请输入选择的数字: ...
- 云计算之路-阿里云上:部分服务器未及时续费造成docker swarm集群故障
非常非常抱歉,由于我们的疏忽 —— docker swarm 集群中的 2 台服务器没有及时续费,造成在夜里0点被自动关机,从而引发整个 docker swarm 集群故障,造成今天凌晨 0:30 ~ ...
- linux_系统调优
linux如何调优? 1. 关闭SELLinux功能,美国国家安全局对于强制访问控制实现,生产场景也是关闭 cat /etc/selinux/config | grep '^SELINUX=' # 查 ...
- java里程碑之泛型--泛型基本语法
1,java7提供的泛型菱形语法 在java7之前,如果使用带泛型的接口和类定义变量初始化对象的时候,构造器后面也必须带上泛型,这有点恶心的.以前我在公司一直使用的java6,所以我也已经习惯了这种写 ...
- JAVA中科学计数法转换普通计数法
今天发现数据库里汇总的数据有点大,一输出就被自动转化成科学计数法了.后来发现是转换字符串的方法有问题.如下: String a = "11234567.89"; BigDecima ...
