这个问题可能适用于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,它将对象置于对象下,作为此“全局”tolua_gc表的键。 当tolua_gc_event函数调用收集器函数(调用delete运算符)时,它将nil值设置为刚删除的对象下的tolua_gc表作为键。那应该有用吗,对吧?
嗯,不。
也许我理解错了,但似乎这对tolua_gc表的大小没有影响。 我也尝试从Lua手动调用tolua.releaseownership(object)。它奏效了。我的意思是,它减少了Lua(LUA_GCCOUNT)使用的内存,但由于它将收集器与对象断开,所以从不调用operator delete,它在C ++中创建了内存泄漏。
这是非常奇怪的行为,因为所有tolua.releaseownership都会将传递对象下的tolua_gc表的nil值设置为键。 那么为什么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.releaseownership:
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
答案 0 :(得分:0)
问题不是bug或内存泄漏。虽然如果内存真的有限,你可以说它是内存泄漏。问题是当你通过将元素设置为nil来移除元素时,tolua_gc(就像它是lua表一样)不会重新散列。
虽然我认为这可能是问题,但我还是愚蠢到不知道它是否属实。因此垃圾收集器不能强制表重新散列并缩小其大小。因此表将增长,直到某些插入将触发重新哈希。 阅读:http://www.lua.org/gems/sample.pdf
所以最后我删除了tolua_gc表,并将metatables(tolua以前用lightboxdata作为键放入tolua_gc表中)作为userdata对象本身的特殊字段。 而且我不是从tolua_gc表中访问那些metatable,而是从对象本身获取它。其他一切都是一样的,似乎有效。