c#-LuaInterface内存泄漏问题

我发现我在C#/ Lua LuaInterface项目中存在严重的内存泄漏.我用C#编写了一个简单的测试函数,该函数每0.5秒从Lua循环调用一次.我看到Lua的内存使用量在每个循环中都在增加.我最新的C#辅助代码是public LuaTable testM...

我发现我在C#/ Lua LuaInterface项目中存在严重的内存泄漏.我用C#编写了一个简单的测试函数,该函数每0.5秒从Lua循环调用一次.我看到Lua的内存使用量在每个循环中都在增加.我最新的C#辅助代码是

  public LuaTable testMemTable()
  {
     LuaTable tabx = m_lua.GetTable("tabx");

     if (tabx != null)
     {
        tabx.Dispose();
        tabx = null;

        GC.Collect();
        GC.WaitForPendingFinalizers();
     }


     m_lua.NewTable("tabx");
     tabx = m_lua.GetTable("tabx");

     for (int i = 0; i < 20000; i++)
        tabx[i] = i * 10;

     return  tabx;
  }

尽管做了tabx.Dispose()和tabx = null然后强行执行GC,我仍然看到内存没有被释放.没有LuaInterface函数可以释放先前分配的表,所以我不知道该怎么做才能释放内存?

Lua边码很简单

while true do

    myAPILibs.testMemTable()

    if tabx ~= nil then
        print(string.format("Size = %d", #tabx))
    else
        print(string.format("Size = nil"))
    end

    print(tabx, tabx[111])

    myAPILibs.sleep(500)

    print("Before ", collectgarbage("count") * 1024)
    collectgarbage("collect")
    print("After ", collectgarbage("count") * 1024)

end

解决我的内存泄漏问题的任何帮助将不胜感激.

再次感谢

杰夫

解决方法:

我需要对LuaInterface稍加抱怨,因为这使这个问题很难解决-在某些情况下,不泄漏内存甚至是不可能的.

LuaInterface(和NLua)中的CLR对象代理没有提供释放Lua引用的终结器.1必须处置每个引用Lua对象的CLR对象.如果您忘记一次,那么Lua对象将永远不会被垃圾回收.

更糟糕的是,您无法正确处理从Lua调用的CLR方法返回的引用.如果您在返回引用之前先处理掉它,那么它就消失了,您将无法再返回它.如果不处理它,则CLR参考会泄漏.您可以执行代码体操来正确处理引用,但是完全没有理由需要您进行此类工作.

现在让我离开我的肥皂盒并解决您的代码.

在所有方法中,都有很多地方会泄漏这些对象.您对GetTable()的使用掩盖了您对其工作方式的误解.每次调用此方法时,都会获得一个新的CLR对象,该对象引用该Lua全局变量引用的Lua表.您对它的用法似乎假设您可以执行m_lua.GetTable(“ tabx”).Dispose()并完成某件事-所有这些操作是创建一个新的CLR引用,然后仅处置该引用.换句话说,这是什么都不做的昂贵方法.

每次调用GetTable()都应进行相应的处理(或使用C#的using块为您进行处理).

要澄清的是,放置CLR LuaTable对象不会破坏Lua表!它所做的只是释放对表的特定CLR参考.

对于引用Lua对象(包括函数)的每个LuaInterface类型,这都是适用的,这是您可能泄漏的另一个地方.

在这里,我将展示每种方法的问题,以及如何将其重写以解决这些问题.

public LuaTable testMemTable()
{
   // This code does nothing.  You get a new reference to the Lua table
   // object, and then you immediately dispose it.  You can remove this
   // whole chunk of code; it's a really expensive no-op.
   LuaTable tabx = m_lua.GetTable("tabx");

   if (tabx != null)
   {
      tabx.Dispose();
      tabx = null;

      GC.Collect();
      GC.WaitForPendingFinalizers();
   }


   m_lua.NewTable("tabx");

   // You create a new CLR object referencing the new Lua table, but you
   // don't dispose this CLR object.
   tabx = m_lua.GetTable("tabx");

   for (int i = 0; i < 20000; i++)
      tabx[i] = i * 10;

   return  tabx;
}

编写此方法的正确方法是:

public void testMemTable()
{
    m_lua.NewTable("tabx");

    using (LuaTable tabx = m_lua.GetTable("tabx")) {
        for (int i = 0; i < 20000; i++) {
            tabx[i] = i * 10;
        }
    }
}

(请注意,由于您从未使用过返回值,因此我将返回类型更改为void.)

public LuaTable getNewTableCSharp()
{
    // You don't dispose the function object.
    var x = lua.GetFunction("getNewTableLua");
    // You don't dispose the table object.
    var retValTab = (LuaTable)x.Call()[0];

    return retValTab;
}

请注意,由于从不重新分配getNewTableLua函数,因此实际上并没有泄漏Lua函数,但是每次调用此函数时,都会泄漏Lua表中包含该函数引用的插槽.

现在麻烦了:因为此函数是从Lua调用并返回对Lua对象的引用,所以您不能修复两个泄漏,只能修复函数泄漏:

public LuaTable getNewTableCSharp()
{
    using (var x = lua.GetFunction("getNewTableLua")) {
        // Still leaks, but you can't do anything about it.
        return (LuaTable)x.Call()[0];
    }
}

回到我的肥皂盒,考虑改用Eluant,它是CLR的一组Lua绑定(非常类似于LuaInterface),只是它解决了内存管??理问题. (免责声明:我是Eluant的作者.)

特别是,它解决了您在这里遇到的问题:

>引用Lua对象的Eluant的CLR对象具有终结器,这些终结器将对Lua引用进行排队,以便稍后发布.如果您忘记放置引用Lua对象的CLR对象,则无论如何最终都会收集它. (但是您仍然应该尽快配置引用,最好使用C#的using块,以确保Lua的GC可以及时收集对象.)
>如果在Lua调用的方法中返回对Lua对象的CLR对象引用,则Eluant将在为Lua返回控制权之前为您处理该引用.

1See here.存在终结器,但如果终结器调用了Lua对象引用,则处理程序不会释放它.换句话说,finalizer根本不执行任何操作.请注意,由于Lua并非线程安全的,因此此时实际释放Lua引用并不安全,但是释放操作可能会在以后排队. LuaInterface不会执行此操作; Eluant does.

本文标题为:c#-LuaInterface内存泄漏问题

基础教程推荐