мне нужно разглагольствовать о LuaInterface немного, потому что это делает эту проблему очень трудно решить - в некоторых случаях это даже невозможно не утечка памяти.
Прокси сайтов CLR в LuaInterface (и NLua) не предоставляют финализаторы, которые освобождают ссылку Lua. Вы должны располагать каждый объект CLR, который ссылается на объект Lua. Если вы забудете один раз, объект Lua никогда не сможет быть собранным мусором.
Хуже того, вы не можете правильно распорядиться ссылкой, которую вы возвращаете из метода CLR, который вызывается Lua. Если вы избавитесь от ссылки, прежде чем вернуть ее, она исчезнет, и вы больше не сможете ее вернуть. Если вы его не уничтожаете, ссылка CLR просочилась. Есть программная гимнастика, которую вы можете сделать, чтобы правильно распоряжаться ссылкой, но нет никаких оснований полагать, что эта работа должна быть необходима с вашей стороны.
Теперь позвольте мне сойти с моего soapbox и обратиться к вашему коду.
Есть много мест во всех ваших методах, где вы просачиваете эти объекты. Ваше использование GetTable()
опровергает ваше непонимание того, как это работает. Каждый раз, когда вы вызываете этот метод, вы получаете новый объект CL12, который ссылается на таблицу Lua, на которую ссылается эта глобальная переменная Lua. Ваше использование этого, кажется, предполагает, что вы можете сделать m_lua.GetTable("tabx").Dispose()
и выполнить что-то - все это делает, это создать новую ссылку CLR, а затем утилизировать только одну ссылку. Другими словами, это дорогой способ вообще ничего не делать.
Каждый вызов GetTable()
должен иметь соответствующее распоряжение (или использовать using
блок C# 's, чтобы сделать удаление для вас).
Для уточнения, распоряжения CLR LuaTable
объект не уничтожить стол Lua! Все, что он делает, - это выпуск , что конкретно CLR ссылается на таблицу.
Это справедливо для каждый Тип LuaInterface, который ссылается на объект Lua, включая функции, который является другим местом, где вы потенциально протекаете.
Здесь я покажу проблемы с каждым методом и как вы перепишете его для решения этих проблем.
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;
}
}
}
(Обратите внимание, что я изменил тип возврата к мочеиспусканию, так как вы никогда не использовать возвращаемое значение.)
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];
}
}
Чтобы получить вернемся на мой soapbox, вместо этого используйте вместо этого Eluant, который представляет собой набор привязок Lua для CLR (как LuaInterface), за исключением того, что он решает проблемы управления памятью. (Отказ от ответственности:. Я являюсь автором элюанта)
В частности, он решает проблемы, вы столкнулись здесь:
- объекты CLR элюанта, что ссылки на объекты Lua имеют финализаторы, которые будут стоять в очереди ссылку Lua на выпустить позже. Если вы забудете удалить объект CLR, ссылающийся на объект Lua, в конечном итоге он будет собран. (Но вы все равно должны распоряжаться ссылками как можно скорее, предпочтительно используя блоки C#
using
, чтобы убедиться, что GC Lua может собирать объекты своевременно.)
- Если вы вернете ссылку объекта CLR объекту Lua в методе, который был вызывается Lua, Eluant будет использовать ссылку для вас, прежде чем он вернет управление Lua.
See here. Финализатор присутствует, но утилита не освобождает ссылку на объект Lua, если она была вызвана финализатором. Другими словами, финализатор в принципе ничего не делает. Обратите внимание, что поскольку Lua не является потокобезопасным, на данный момент небезопасно выпускать ссылку Lua, но операция выпуска может быть поставлена в очередь на последующие. LuaInterface этого не делает; Eluant does.