2016-11-07 246 views
2

我發現我在C#/ Lua LuaInterface項目中有一個錯誤的內存泄漏。我已經在C#中寫了一個簡單的測試函數,每隔0.5秒從Lua中調用一次。我可以看到每個循環都會增加Lua內存使用量。我最新的C#端代碼LuaInterface內存泄漏問題

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 

任何幫助解決我的內存泄漏問題將不勝感激。

再次感謝

傑夫

回答

1

我要咆哮約LuaInterface一點,因爲它使這個問題很難解決 - 在某些情況下,它甚至不可能不泄漏內存。

LuaInterface(和NLua)中的CLR對象代理不提供釋放Lua引用的終結器。 您必須處理引用Lua對象的每個CLR對象。如果你忘記一次,Lua對象永遠不會被垃圾收集。

更糟的是,您無法正確處理從Lua調用的CLR方法返回的引用。如果您在退回參考文獻之前丟棄該參考文件,則該參考文件不存在,您無法再將其退回。如果您不處理它,則CLR參考被泄漏。有代碼體操,你可以做適當的處理參考,但沒有任何理由應該需要你的這種工作。

現在讓我從我的肥皂箱下載並解決您的代碼。

在泄漏這些對象的所有方法中有很多地方。您對GetTable()的使用掩蓋了您對其工作原理的誤解。每次調用此方法時,都會得到一個新的 CLR對象,該對象引用由該Lua全局變量引用的Lua表。您的使用似乎假設您可以執行m_lua.GetTable("tabx").Dispose()並完成某些操作 - 所有這些操作都會創建一個新的CLR參考,然後僅處理一個參考即可處理。換句話說,這是一種無所作爲的昂貴方式。

GetTable()每次調用應該有一個相應的處置(或使用C#的using塊做處理你)。

澄清,配置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; 
     } 
    } 
} 

(請注意,我改變了返回類型爲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]; 
    } 
} 

要獲得回到我的soapbox上,考慮使用Eluant來代替,它是CLR的一組Lua綁定(非常像LuaInterface),只是它解決了內存管​​理問題。 (免責聲明:我洗脫液的作者)

特別是,它解決了你遇到了這裏的問題:

  • 洗脫液的CLR對象引用的Lua對象有終結,將排隊的Lua參考稍後發佈。如果你忘記處理一個引用Lua對象的CLR對象,它最終會被收集起來。 (但你仍然應該儘快處理引用,最好使用C#的using塊,以確保Lua的GC可以及時收集對象。)
  • 如果您返回一個CLR對象引用到一個方法中的Lua對象由Lua調用,Eluant會在您將控制權返還給Lua之前爲您處置參考。

See here。終結器存在,但如果終結器調用了處理器,它不會釋放Lua對象引用。換句話說,終結者基本上什麼都不做。請注意,由於Lua不是線程安全的,因此在此時實際釋放Lua引用並不安全,但釋放操作可能會在稍後排隊。 Lua接口不這樣做; Eluant does

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的實例加載的,因爲它被重置的方式表的多個版本。