我发现在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;
}
任何帮助解决我的内存泄漏问题将不胜感激。
再次感谢
杰夫
答案 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的作者。)
特别是,它解决了您遇到的问题:
using
块,以确保Lua的GC能够及时收集对象。)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实例正在加载表的多个版本,因为它被重置的方式。