这个问题可能是针对 Lua 和 tolua 专家的。
我正在使用 tolua++1.0.93 和 lua-5.1.4(CEGUI 0.84 依赖项)。我已经跟踪这个讨厌的内存泄漏几个小时了,我发现 toluapp 在 Lua 注册表中创建了 tolua_gc 表,并且该表似乎无限增长。
当我使用 tolua_pushusertype_and_takeownership 将我的对象推送到 Lua 时,我希望 Lua 的 GC 删除我的对象。它确实这样做了,但是 tolua_pushusertype_and_takeownership 调用 tolua_register_gc ,它把这个对象在 object 作为这个“全局” tolua_gc 表的键。当 tolua_gc_event 函数调用收集器函数(调用删除运算符)时,它将 nil 值设置为刚刚删除的对象下的 tolua_gc 表作为键。所以这应该有效,对吧?
嗯,不。
也许我理解错了,但这似乎对 tolua_gc 表的大小没有影响。我也尝试过从 Lua 手动调用 tolua.releaseownership(object) 。它奏效了。我的意思是,它减少了 Lua (LUA_GCCOUNT) 使用的内存,但由于它断开了收集器与对象的连接,因此永远不会调用 operator delete 并且它在 C++ 中造成内存泄漏。
这真的很奇怪,因为 tolua.releaseownership 所做的只是将 nil 值设置为传递对象下的 tolua_gc 表作为键。那么为什么 tolua.releaseownership 会减少 Lua 使用的内存大小,而 tolua_gc_event 不会呢?唯一的区别是 tolua.releaseownership 在将 nil 设置为 tolua_gc 表之前调用垃圾收集器,而 tolua_gc_event 由垃圾收集器调用(相反情况)。
为什么我们需要那个全局 tolua_gc 表?我们不能在收集的时候直接从对象中获取元表吗?
我可以从这个进程中使用的内存真的很有限(8MB),而且似乎这个 tolua_gc 表在一段时间后占据了它的 90%。
我怎样才能解决这个问题?
谢谢你。
编辑:这些是代码示例:
extern unsigned int TestSSCount;
class TestSS
{
public:
double d_double;
TestSS()
{
// TestSSCount++;
// fprintf(stderr, "c(%d)\n",TestSSCount);
}
TestSS(const TestSS& other)
{
d_double = other.d_double * 0.5;
// TestSSCount++;
// fprintf(stderr, "cc(%d)\n",TestSSCount);
}
~TestSS()
{
// TestSSCount--;
// fprintf(stderr, "d(%d)\n", TestSSCount);
}
};
class App
{
...
TestSS doNothing()
{
TestSS t;
t.d_double = 13.89;
return t;
}
void callGC()
{
int kbs_before = lua_gc(d_state, LUA_GCCOUNT, 0);
lua_gc(d_state, LUA_GCCOLLECT, 0);
int kbs_after = lua_gc(d_state, LUA_GCCOUNT, 0);
printf("GC changed memory usage from %d kB to %d kB, difference %d kB",
kbs_before, kbs_after, kbs_before - kbs_after);
}
...
};
这是 .pkg 文件:
class TestSS
{
public:
double d_double;
};
class App
{
TestSS doNothing();
void callGC();
};
现在完成 Lua 代码(app 和 rootWindow 是作为常量提供给 Lua 的 C++ 对象):
function handleCharacterKey(e_)
local key = CEGUI.toKeyEventArgs(e_).scancode
if key == CEGUI.Key.One then
for i = 1,10000,1 do
-- this makes GC clear all memory from Lua heap but does not call destructor in C++
-- tolua.releaseownership(app:doNothing())
-- this makes GC call destructors in C++ but somehow makes Lua heap increase constantly
app:doNothing()
elseif key == CEGUI.Key.Zero then
app:callGC()
end
end
rootWindow:subscribeEvent("KeyUp", "handleCharacterKey")
这是我按下 0 1 0 1 0 1 0 时得到的输出:
这是我使用 tolua.releaseowenership 的时候
GC changed memory usage from 294 kB to 228 kB, difference 66 k
GC changed memory usage from 228 kB to 228 kB, difference 0 kB
GC changed memory usage from 228 kB to 228 kB, difference 0 kB
GC changed memory usage from 228 kB to 228 kB, difference 0 kB
这没有 tolua.release 所有权:
GC changed memory usage from 294 kB to 228 kB, difference 66 kB
GC changed memory usage from 605 kB to 604 kB, difference 1 kB
GC changed memory usage from 982 kB to 861 kB, difference 121 kB
GC changed memory usage from 1142 kB to 1141 kB, difference 1 kB
这没有发布所有权,但我在键盘上按下的序列是 0 1 0 1 0 1 0 0 0 0 (最后对 GC 的三个额外调用)
GC changed memory usage from 294 kB to 228 kB, difference 66 kB
GC changed memory usage from 603 kB to 602 kB, difference 1 kB
GC changed memory usage from 982 kB to 871 kB, difference 111 kB
GC changed memory usage from 1142 kB to 1141 kB, difference 1 kB
GC changed memory usage from 1141 kB to 868 kB, difference 273 kB <- this is after first additional GC call
GC changed memory usage from 868 kB to 868 kB, difference 0 kB
GC changed memory usage from 868 kB to 868 kB, difference 0 kB