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內存分析工具