Lua Lua記憶體洩露檢測原理

2022-09-24 01:09:12 字數 4093 閱讀 5943

首先第一點,lua中的記憶體洩露和我們所說的c/c++中的記憶體洩露本質上是不一樣的。

lua中有垃圾**機制(gc),所以理論上是不會有記憶體洩露的。當它進行gc的時候,會從根部開始掃瞄所有的物件,如果某個地方對這個物件還有引用,就不會把這個物件記憶體collect,這個物件就沒有被gc。所以lua中的記憶體洩露是指那些:已經沒有被使用了,但外部依然還有引用存在的物件。

--函式中應該被申明為local的物件忘記加local

local function test()

testtable = {} --這個testtabel會被存放在全域性表_g中,gc時由於此物件還有引用存在,所以這裡總是會有乙個table洩露。

local mt = {} --mt加了local修飾,函式呼叫完後,引用也不復存在了,gc時會被**。

setmetatable(testtable, mt)

endlua中支援垃圾**機制的物件有五種:string,table,function,full userdata,thread。而他們的引用直接或間接的儲存到:lua_state物件,_g全域性表,registry登錄檔,global_state->mt中。

在指令碼中:

執行的lua指令碼本身就是lua_state。_g就是_g全域性表。registry表可以用debug.getregistry獲取。global_mt可以用debug.getmetatable獲取。

所以我們就可以在指令碼層次實現記憶體洩露的檢測模組。

在搜尋時需要注意的幾點:

table 額外搜尋metatable,若metatable中的__mode取值為」k"、"v"或者」kv"需特殊處理(補充中有說明)function 額外搜尋 enviroment,也是乙個table。額外搜尋upvalues,這個可以是任何型別。由於userdata在script層次不能被修改,所以搜搜他的metatable吧thread物件就是coroutine物件,在script中一般都不會建立多個coroutine,所以在指令碼中沒搜尋它。若是需求的話,獲取到它的執行緒函式,然後再按照第2步操作就可以了。

在檢測洩露之前,先搜尋一下所有的物件,儲存好起始的記憶體狀態,在程式執行之後執行幾次gc操作,然後再進行一次搜尋,對比兩次的結果,多出來的那些就有可能是記憶體洩露了。

補充:lua中有一種叫weak表的東東,它的metatable中的__mode被設定為「k","v"或者」kv",表示儲存在它中的鍵或值或鍵值都是一種弱引用狀態。

若乙個物件的所有引用都是弱引用了,那麼這個物件也會被gc**掉,所以對應的weak表中此物件的入口就沒有了。

所以我們可以用另外一種實現:就是把使用者自己建立的資源物件統統都丟到weak表中,執行完程式後強制gc,然後去檢視weak表,若表中還儲存著那個物件,就意味著這個物件還有外部引用(相對弱引用我們就叫它為強引用吧),資源沒有被gc掉,所以我們可以說這個物件很有可能是記憶體洩露了。

lua的gc演算法使用的所謂「mark and sweep」演算法。簡單的理解,這個演算法將gc分為兩個階段,乙個是標記(mark)階段,這一階段將所有系統中引用的物件都逐一標記;而在清理(sweep)階段,將把在mark階段中沒有被標記的資料刪除。

在lua中,使用幾種顏色來區分不同的結點:

white:白色表示沒有進行過標記的節點gray:灰色表示已經進行過標記的節點,但是與它相關聯的節點還沒有進行過標記。black:本節點和與之關聯的節點都已經被掃瞄標記過了。

通常會出現有關聯資料的,包括有table,upvalue等資料型別。collectgarbage函式提供了多項功能:停止垃圾**,重啟垃圾**,強制執行一次**迴圈,強制執行一步垃圾**,獲取lua占用的記憶體,以及兩個影響垃圾**頻率和步幅的引數。collectgarbage(opt,[,arg])

"stop"

停止垃圾收集器,如果它的執行。

"restart"

如果垃圾收集器已經停止,將重新啟動它。

"collect"

執行一次全垃圾收集迴圈。預設執行此操作

"count"

返回當前lua中使用的記憶體量(以kb為單位)

"step"

單步執行乙個垃圾收集. 步長 "size" 由引數arg指定 (大型的值需要多步才能完成),如果要準確指定步長,需要多次實驗以達最優效果。如果步長完成一次收集迴圈,將返回true

"setpause"

設定 arg/100 的值作為暫定收集的時長;並返回設定前的值。預設為200

控制了收集器在開始乙個新的收集週期之前要等待多久。 隨著數字的增大就導致收集器工作工作的不那麼主動。 小於 1 的值意味著收集器在新的週期開始時不再等待。 當值為 2 的時候意味著在總使用記憶體數量達到原來的兩倍時再開啟新的週期。

"setstepmul"

設定 arg/100 的值,作為步長的增幅(即新步長=舊步長*arg/100);並返回設定前的值。預設為200

控制了收集器的工作速度,這個速度是乙個相對於記憶體分配的速度。更大的數字將導致收集器工作的更主動的同時,也使每步收集的尺寸增加。 小於 1 的值會使收集器工作的非常慢,可能導致收集器永遠都結束不了當前週期。 預設值為200%,這意味著收集器將以記憶體分配器的兩倍速執行。

function test1()

collectgarbage("collect")--為了有乾淨的環境,先把可以收集的垃圾收集了

collectgarbage()--為了保證記憶體的收集的相對乾淨,及記憶體的穩定,要執行多次收集

print("now,lua記憶體為:",collectgarbage("count")) -->205.7158203125 kb

local colen = {} --現在是區域性變數

for i=1,5000 do

table.insert(colen,{})

endprint("now,lua記憶體為:",collectgarbage("count"))-->860.4111328125 kb

--建立5000個table,記憶體增加了655 kb

endfunction collect1()

print("now,lua記憶體為:",collectgarbage("count"))-->608.060546875 kb

collectgarbage()

collectgarbage()

print("now,lua記憶體為:",collectgarbage("count"))-->204.8408203125 kb

--最後與一開始只差只有1kb

endfunction test2()

collectgarbage("collect")--為了有乾淨的環境,先把可以收集的垃圾收集了

collectgarbage()--為了保證記憶體的收集的相對乾淨,及記憶體的穩定,要執行多次收集

print("now,lua記憶體為:",collectgarbage("count")) -->205.7158203125 kb

colen = {} --現在是全部變數

for i=1,5000 do

table.insert(colen,{})

endprint("now,lua記憶體為:",collectgarbage("count"))-->619.826171875 kb

--建立5000個table,記憶體增加了414 kb;這些增加的記憶體,由於已放到了全域性函式中,是永遠沒有機會被**到了!

endfunction collect2()

print("now,lua記憶體為:",collectgarbage("count"))-->596.7822265625 kb

collectgarbage()

collectgarbage()

collectgarbage()

print("now,lua記憶體為:",collectgarbage("count"))-->489.189453125 kb

--最後記憶體增加了284kb(489-205)

end垃圾**器有兩個引數用於控制它的節奏:

第乙個引數,稱為暫停時間,控制**器在完成一次**之後和開始下次**之前要等待多久;

第二個引數,稱為步進係數,控制**器每個步進**多少內容。粗略地來說,暫停時間越小、步進係數越大,垃圾**越快。這些引數對於程式的總體效能的影響難以**,更快的垃圾**器顯然會浪費更多的cpu週期,但是它會降低程式的記憶體消耗總量,並可能因此減少分頁。只有謹慎地測試才能給你最佳的引數值。

記憶體洩露檢測

c 中檢測記憶體洩漏可以引入系統定義的巨集來檢視,內存在哪個位置洩漏 檔案開始處加入下列定義 define crtdbg map alloc include include 程式退出時加入以下函式 crtdumpmemoryleaks 如果有洩漏會顯示 記憶體洩漏是程式設計中常常見到的乙個問題,我所...

檢測記憶體洩露

程式結束時,作業系統會 程式占用的資源.但是,只要程式還在執行,如果不進行清理,資源最終可能被耗盡.1.vc記憶體洩露檢查工具 visual leak detector 現在已知的最新有2.0版本的,使方法不詳。使用 visual leak detector 2.2.3 在vs工程的linker i...

記憶體洩露檢測

1 包含標頭檔案 include include 2 每個cpp檔案包含 static char this file file define new new normal block,this file,line 3 設定標誌 int tmpdbgflag tmpdbgflag crtsetdbgf...