最近给公司写了一个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复杂一些

  1. 因为要操作Lua栈,稍微写错一个栈没对称弹出,就会导致溢出,调试起来非常麻烦
  2. 因为c语言就像一块空地,什么都要自己造,连一些最基本的数据结构,都没有...
  3. 你需要编译成各个平台的库,这个后面会讲到如何跟tolua c编译到一起
工具分为2个部分
  1. c库生成快照
  2. web端接收上传快照,快照分析
    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, co