gpt4 book ai didi

c# - LuaInterface 内存泄漏问题

转载 作者:行者123 更新时间:2023-11-30 16:00:17 25 4
gpt4 key购买 nike

我发现我在 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 block 为您进行处理)。

澄清一下,处理 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 绑定(bind)(很像 LuaInterface),只是它解决了内存管​​理问题。 (免责声明:我是 Eluant 的作者。)

特别是,它解决了您在这里遇到的问题:
  • 引用 Lua 对象的 Eluant 的 CLR 对象具有终结器,它们会将 Lua 引用排队等待稍后释放。如果你忘记释放一个引用 Lua 对象的 CLR 对象,它最终还是会被回收。 (但您仍应尽快处理引用,最好使用 C# 的 using block ,以确保 Lua 的 GC 可以及时收集对象。)
  • 如果您在 Lua 调用的方法中返回对 Lua 对象的 CLR 对象引用,Eluant 将在将控制权返回给 Lua 之前为您处理该引用。


  • 1 See here .终结器存在,但如果 Lua 对象引用被终结器调用,则处理器不会释放它。换句话说,终结器基本上什么都不做。请注意,由于 Lua 不是线程安全的,此时实际释放 Lua 引用是不安全的,但释放操作可以排队等待稍后。 LuaInterface 不这样做; Eluant does .

    关于c# - LuaInterface 内存泄漏问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40456209/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com