程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> [Lua]Lua內存洩露檢測原理

[Lua]Lua內存洩露檢測原理

編輯:關於C++

lua內存洩露

首先第一點,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) 
end

檢測原理

lua中支持垃圾回收機制的對象有五種: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步操作就可以了。

搜索流程圖(_G表)

\

在檢測洩露之前,先搜索一下所有的對象,保存好起始的內存狀態,在程序執行之後執行幾次GC操作,然後再進行一次搜索,對比兩次的結果,多出來的那些就有可能是內存洩露了。 <喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+srmz5KO6PC9wPgo8cD5sdWHW0NPQ0rvW1r3Qd2Vha7HttcS2q7aro6zL/LXEbWV0YXRhYmxl1tC1xF9fbW9kZbG7yejWw86qobBr","v"或者”kv",表示保存在它中的鍵或值或鍵值都是一種弱引用狀態。
若一個對象的所有引用都是弱引用了,那麼這個對象也會被GC回收掉,所以對應的weak表中此對象的入口就沒有了。

所以我們可以用另外一種實現:就是把用戶自己創建的資源對象統統都丟到weak表中,運行完程序後強制GC,然後去查看weak表,若表中還保存著那個對象,就意味著這個對象還有外部引用(相對弱引用我們就叫它為強引用吧),資源沒有被GC掉,所以我們可以說這個對象很有可能是內存洩露了。

Lua垃圾回收算法

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,{})
    end
    print("now,Lua內存為:",collectgarbage("count"))-->860.4111328125 KB
    --創建5000個table,內存增加了655 KB
end

function collect1()
    print("now,Lua內存為:",collectgarbage("count"))-->608.060546875 KB
    collectgarbage()
    collectgarbage()
    print("now,Lua內存為:",collectgarbage("count"))-->204.8408203125 KB
    --最後與一開始只差只有1KB
end
function test2()
    collectgarbage("collect")--為了有干淨的環境,先把可以收集的垃圾收集了
    collectgarbage()--為了保證內存的收集的相對干淨,及內存的穩定,要執行多次收集
    print("now,Lua內存為:",collectgarbage("count")) -->205.7158203125 KB
    colen = {} --現在是全部變量
    for i=1,5000 do
        table.insert(colen,{})
    end
    print("now,Lua內存為:",collectgarbage("count"))-->619.826171875 KB
    --創建5000個table,內存增加了414 KB;這些增加的內存,由於已放到了全局函數中,是永遠沒有機會被回收到了!
end

function 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周期,但是它會降低程序的內存消耗總量,並可能因此減少分頁。只有謹慎地測試才能給你最佳的參數值。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved