我发现我在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内存泄漏问题
基础教程推荐
- C# RabbitMQ的使用详解 2023-05-12
- C#中的应用程序接口介绍及实现,密封类与密封方法 2023-01-06
- C#多线程系列之多阶段并行线程 2023-05-22
- unity avprovideo插件的使用详解 2023-04-14
- 基于C#实现微信支付宝扫码支付功能 2023-01-27
- C#9新特性init only setter的使用 2023-03-28
- Unity实现虚拟键盘 2023-04-09
- C#构建树形结构数据(全部构建,查找构建) 2022-11-19
- C#各种异常处理方式总结 2023-07-04
- winform实现五子棋游戏 2022-12-06