Lua5.2採用垃圾回收機制對所有的lua對象(GCObject)進行管理。Lua虛擬機會定期運行GC,釋放掉已經不再被被引用到的lua對象。
基本算法
基本的垃圾回收算法被稱為"mark-and-sweep"算法。算法本身其實很簡單。
首先,系統管理著所有已經創建了的對象。每個對像都有對其他對象的引用。root集合代表著已知的系統級別的對象引用。我們從root集合出發,就可以訪問到系統引用到的所有對象。而沒有被訪問到的對象就是垃圾對象,需要被銷毀。
我們可以將所有對象分成三個狀態:
- White狀態,也就是待訪問狀態。表示對像還沒有被垃圾回收的標記過程訪問到。
- Gray狀態,也就是待掃描狀態。表示對像已經被垃圾回收訪問到了,但是對象本身對於其他對象的引用還沒有進行遍歷訪問。
- Black狀態,也就是已掃描狀態。表示對像已經被訪問到了,並且也已經遍歷了對象本身對其他對象的引用。
基本的算法可以描述如下:
- 當前所有對像都是White狀態;
- 將root集合引用到的對像從White設置成Gray,並放到Gray集合中;
- while(Gray集合不為空)
- {
- 從Gray集合中移除一個對象O,並將O設置成Black狀態;
- for(O中每一個引用到的對象O1) {
- if(O1在White狀態) {
- 將O1從White設置成Gray,並放到到Gray集合中;
- }
- }
- }
- for(任意一個對象O){
- if(O在White狀態)
- 銷毀對象O;
- else
- 將O設置成White狀態;
- }
Incremental Garbage Collection
上面的算法如果一次性執行,在對像很多的情況下,會執行很長時間,嚴重影響程序本身的響應速度。其中一個解決辦法就是,可以將上面的算法分步執行,這樣每個步驟所耗費的時間就比較小了。我們可以將上述算法改為以下下幾個步驟。
首先標識所有的root對象:
- 當前所有對像都是White狀態;
- 將root集合引用到的對像從White設置成Gray,並放到Gray集合中;
遍歷訪問所有的gray對象。如果超出了本次計算量上限,退出等待下一次遍歷:
- <p>while(Gray集合不為空,並且沒有超過本次計算量的上限)</p>{
- 從Gray集合中移除一個對象O,並將O設置成Black狀態;
- for(O中每一個引用到的對象O1) {
- if(O1在White狀態) {
- 將O1從White設置成Gray,並放到到Gray集合中;
- }
- }
- }
銷毀垃圾對象:
- for(任意一個對象O){
- if(O在White狀態)
- 銷毀對象O;
- else
- 將O設置成White狀態;
- }
在每個步驟之間,由於程序可以正常執行,所以會破壞當前對象之間的引用關係。black對象表示已經被掃描的對象,所以他應該不可能引用到一個white對象。當程序的改變使得一個black對象引用到一個white對象時,就會造成錯誤。解決這個問題的辦法就是設置barrier。barrier在程序正常運行過程中,監控所有的引用改變。如果一個black對象需要引用一個white對象,存在兩種處理辦法:
- 將white對象設置成gray,並添加到gray列表中等待掃描。這樣等於幫助整個GC的標識過程向前推進了一步。
- 將black對象該回成gray,並添加到gray列表中等待掃描。這樣等於使整個GC的標識過程後退了一步。
這種垃圾回收方式被稱為"Incremental Garbage Collection"(簡稱為"IGC",Lua所採用的就是這種方法。使用"IGC"並不是沒有代價的。IGC所檢測出來的垃圾對象集合比實際的集合要小,也就是說,有些在GC過程中變成垃圾的對象,有可能在本輪GC中檢測不到。不過,這些殘餘的垃圾對像一定會在下一輪GC被檢測出來,不會造成洩露。
No comments:
Post a Comment