1. 程式人生 > >Lua原始碼分析 Gc篇(一)原理

Lua原始碼分析 Gc篇(一)原理

前言
原理
mark階段
sweep階段
三種顏色
資料流
參考

前言

已經有很多人寫了gc原始碼分析的文章了,自己為啥還要繼續寫呢?最主要的原因有兩個:

  • 1.純屬對於個人來說,寫東西能夠加深自己的理解和記憶,是個再學習的過程
  • 2.看了一圈,雲風系列和[email protected]的系列文章比較好,已經很詳細了,但是自己看的過程中還是有些問題,他們沒有詳細介紹,可能是覺得問題太小了,或者在他們根本不是問題。因為後面回頭看來,的確不是什麼問題,自己再仔細想一下或許不會有那些問題。但是,借用"勿以善小而不為,勿以惡小而為之"的意思,覺得這些小點還是值得記錄下來

原理

mark & sweep演算法,維基上的詞條叫Tracing garbage collection[1].

根據名字也可以知道,最主要包括兩個階段:mark階段和sweep階段。

mark階段

這個階段叫做掃描階段。簡單來講,就是對於現在lua用到的所有物件進行掃描一次。如果某個物件當前跟別的物件有引用關係,那麼說明他還在用;如果某個物件跟其他任何物件都沒有引用關係了,說明這個物件已經沒有用了。這個階段做完,就可以知道哪些物件還有用,哪些物件不再使用了,下面就交給下一個階段,sweep階段。

注意:這裡說到的引用關係,很多文章沒有解釋清楚,只是大概說明了一下。這裡先簡單介紹一下,lua的掃描階段雖然最終是對所有物件掃描了一遍,但是並不是遍歷gc連結串列(這個連結串列放了lua所有的可回收的物件),而是先把最頂層的幾個節點放在一個池子(另外一個連結串列)裡面,然後遞迴深入這個物件,新遍歷到的物件說明有引用關係。把新遍歷的物件放入池子,繼續遞迴深入遍歷,這樣就等於把所有有引用關係的物件遍歷了一遍,那麼沒有遍歷過的物件就自然是跟其他任何物件都沒有引用關係的了。

sweep階段

這個階段做的事情其實很少,關鍵步驟在前一個階段做完了。這個階段根據前一個掃描階段得到的結果,遍歷一遍所有物件。如果這個物件已經標記為不再使用了,就會被清理掉,釋放掉所在的記憶體;如果這個物件還在使用,那麼就處理一下狀態,等待下一次gc在處理。

這裡說的很簡單,至於掃描階段做了什麼,怎麼個標記法;以及處理階段又具體做了什麼,被引用的物件,清理狀態又幹了什麼,這些細節等待後面深入lua原始碼的時候再一一分析。

如果mark階段一次性把所有節點都掃描,再一次性清理完,那麼這兩個步驟就都很簡單了。但是,這樣就有效率問題,一次性要把所有物件處理一遍,在大工程裡面就絕對是一個瓶頸。所以,lua5.0以後就把gc改成了增量式的gc,主要是把標記擴充套件成了三種顏色,下面詳細介紹一下。

三種顏色

三種顏色會讓lua的gc,主要是掃描和清理階段不再簡單粗暴,而是一次性處理一部分,可以隨時中斷,下次再進來處理。三種顏色:包括白, 灰和黑。[2]

注意白色是有兩種,後面再詳細介紹。

  • (1)White 
    表示當前物件為待訪問狀態,用於表示物件還沒有被GC的標記過,這也是任何一個Lua物件在建立之後的初始狀態,換言之,如果一個物件,在一個GC掃描過程完畢之後,仍然是白色的,那麼說明該物件沒有被系統中任何一個物件所引用,可以回收其空間了。也就是前面提到的,和其他物件沒有引用關係,那麼一次gc過程中就不會被掃描。
  • (2)Gray 
    表示當前物件為待掃描狀態,用於表示物件已經被GC訪問過,但是該物件引用的其他物件還沒有被訪問到。這個灰色,就是後來增加的顏色,是一箇中間狀態。也就是這個灰色增加的狀態能夠讓gc過程能被終端,比如某個物件遞迴深入掃描中間的時候,能夠中斷。比如一個table,掃描了部分key-value之後,還有一部分沒有掃描,那麼這個table就是灰色的,等下次gc進來,接著上一次的gc流程,繼續掃描剩下的table部分。
  • (3)Black 
    表示當前物件為已掃描狀態,用於表示物件已經被GC訪問過,並且該物件引用的其他物件也已經被訪問過了。

這個地方提個小問題思考一下:某個物件遞迴深入掃描完之後,說明他肯定是黑色的,是不是就可以不管他了呢?

資料流

下面給出一個數據流的圖[2],詳細的內容後面的章節再繼續闡述。 

參考

[1]Tracing garbage collection 
[2]ch08-GC