当使用tolua_pushusertype_and_takeownership时,toluapp是否会造成内存泄漏?

时间:2016-02-18 18:12:13

标签: c++ memory-leaks lua garbage-collection tolua++

这个问题可能适用于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

1 个答案:

答案 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,而是从对象本身获取它。其他一切都是一样的,似乎有效。