LuaInterface内存泄漏问题

时间:2016-11-07 00:36:09

标签: c# memory-leaks lua

我发现在C#/ Lua LuaInterface项目中我有一个糟糕的内存泄漏。我在C#中编写了一个简单的测试函数,每隔0.5秒从一个循环中调用Lua。我可以看到每个循环的Lua内存使用量都在增加。我最新的C#端代码是

<td align = "left"><input type="textarea" name="post" placeholder="What are you thinking? (Only one tag allowed)" style="width: 330px; height: 50px;"  value="<?php echo $post;?>" /> </td>
<td align = "center"><input type="text" name="tag" placeholder="Put your tags here!" style="width: 130px; height: 50px;" value="<?php echo $tag;?>" /> </td>   
            </tr>
        </table>
        <table bgcolor="#004A00" align="center" width="650px">
             <tr>
                <td><br /></td>
            </tr>
            <tr>
              <div align = "center">
            <!-- Input For Add Values To Database-->
            <input type="image" src="http://www.swiftpic.org/image.uploads/06-11-2016/original-655b6e1a6e4b4c149b9969ad99e65699.png"" name="insert">

            <!-- Input For Edit Values -->
            <input type="image" src="http://www.swiftpic.org/image.uploads/06-11-2016/original-80a74827d6700e084482680251134c90.png" name="update">

            <!-- Input For Clear Values -->
            <input type="image" src="http://www.swiftpic.org/image.uploads/06-11-2016/original-02f5a241ad8d3f6c043076e2bb0fce7f.png" name="delete">

            <!-- Input For Find Values With The given ID -->
            <input type="image" height="60px" src="http://www.swiftpic.org/image.uploads/06-11-2016/original-a2aafe78a53258d59b7421b3cf266eb2.png" name="search">
            <a href="?orderBy=date"> Date Ascending:</a>
            <a href="?orderBy1=date"> Date Descending:</a>
       </div>    

尽管做了tabx.Dispose()和tabx = null然后强制GC我仍然看到内存没有被释放。没有LuaInterface函数可以释放以前分配的表,所以我还可以做些什么来释放内存?

Lua边代码非常简单

  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;
  }

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

再次感谢

杰夫

2 个答案:

答案 0 :(得分:2)

我需要稍微喋喋不休地谈论LuaInterface,因为这使得这个问题很难解决 - 在某些情况下,甚至不可能不泄漏内存。

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

更糟糕的是,您无法正确处理从Lua调用的CLR方法返回的引用。如果您在退回之前丢弃该参考,它就会消失,您将无法再返回该参考。如果您不进行处理,则会泄露CLR参考。您可以使用代码体操来妥善处理参考,但没有理由认为您需要这类工作。

现在让我离开我的肥皂盒并解决你的代码。

在所有方法中有许多地方泄漏这些对象。您对GetTable()的使用掩盖了您对其运作方式的误解。每次调用此方法时,都会得到一个 new CLR对象,该对象引用该Lua全局变量引用的Lua表。您对它的使用似乎假设您可以执行m_lua.GetTable("tabx").Dispose()并完成某些操作 - 所有这一切都是创建一个新的CLR引用,然后只处理一个引用。换句话说,这是一种无所事事的昂贵方式。

GetTable()的每次调用都应该有相应的处置(或使用C#&#39; 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对象的引用,你无法解决这两个漏洞,你只能修复函数泄漏:< / p>

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之前为您配置引用。

1 See here。终结器存在,但如果终结器调用它,则处理器不会释放Lua对象引用。换句话说,终结器基本上根本不做任何事情。请注意,因为Lua不是线程安全的,所以此时实际释放Lua引用是不安全的,但是释放操作可以排队等待以后。 LuaInterface不这样做; Eluant does

答案 1 :(得分:1)

简短回答

lua.newTable("testTable")替换为
lua.doString("for key,value in ipairs(testTable) do testTable[key]=nil end");

没有内存泄漏,您的代码运行正常。

答案很长

让我们说你有这样的lua脚本:

newTable = {1,2,3,4,5,6,7,8,9,10,11,12,13,14}   
newTable = {}   

如果你在Lua方面这样做,即使你从未真正清除原始表,也没有泄漏。 Lua不知何故知道在重新分配之前处理该表。

但是,如果在将表设置为空表之前对表有任何C#引用,即使lua处理它的内容,也不会丢弃C#引用;显然它就像实例创建了一个全新的表一样。您需要做的是重置表:

for key,value in ipairs(testTable) do   
   testTable[key] = nil   
end

你的问题实际上不是内存泄漏,而是你的静态Lua实例正在加载表的多个版本,因为它被重置的方式。