Lua中的GC

原文地址:https://www.cnblogs.com/zblade/p/8824376.html

原文地址:https://blog.csdn.net/BigBrick/article/details/85317491

原文地址:https://blog.codingnow.com/2018/10/lua_gc.html

一、GC的原理及其算法設計

不同的語言,對GC算法的設計不同,常見的GC算法是引用計數和Mark-Sweep算法, c#採用的是Mark-sweep && compact算法, Lua採用的是Mark-sweep算法,分開說一下:

引用計數算法:在一個對象被引用的情況下,將其引用計數加1,反之則減1,如果計數值爲0,則在GC的時候回收,這個算法有個問題就是循環引用。

Mark-sweep算法(標記清除法):每次GC的時候,對所有對象進行一次掃描,如果該對象不存在引用,則被回收,反之則保存。

在Lua5.0及其更早的版本中,Lua的GC是一次性不可被打斷的過程,使用的Mark算法是雙色標記算法(Two color mark),這樣系統中對象的非黑即白,要麼被引用,要麼不被引用,這會帶來一個問題:在GC的過程中如果新加入對象,這時候新加入的對象無論怎麼設置都會帶來問題,如果設置爲白色,則如果處於回收階段,則該對象會在沒有遍歷其關聯對象的情況下被回收;如果標記爲黑色,那麼沒有被掃描就被標記爲不可回收,是不正確的。

爲了降低一次性回收帶來的性能問題以及雙色算法的問題,在Lua5.1後,Lua都採用分佈回收以及三色增量標記清除算法(Tri-color incremental mark and sweep)

白色:可回收狀態。
詳解:如果該對象未被GC標記過則此時白色代表當前對象爲待訪問狀態。舉例:新創建的對象的初始狀態就應該被設定爲白色,因爲該對象還沒有被GC標記到,所以保持初始狀態顏色不變,仍然爲白色。如果該對象在GC標記階段結束後,仍然爲白色則此時白色代表當前對象爲可回收狀態。但其實本質上白色的設定就是爲了標識可回收。
灰色:中間狀態。
詳解:當前對象爲待標記狀態。舉例:當前對象已經被GC訪問過,但是該對象引用的其他對象還沒有被標記(新創建的引用)。
黑色:不可回收狀態。
詳解:當前對象爲已標記狀態。舉例:當前對象已經被GC訪問過,並且對象引用的其他對象也被標記了。
備註:白色分爲白1和白2。原因:在GC標記階段結束而清除階段尚未開始時,如果新建一個對象,由於其未被發現引用關係,原則上應該被標記爲白色,於是之後的清除階段就會按照白色被清除的規則將新建的對象清除。這是不合理的。於是lua用兩種白色進行標識,如果發生上述情況,lua依然會將新建對象標識爲白色,不過是“當前白”(比如白1)。而lua在清掃階段只會清掃“舊白”(比如白2),在清掃結束之後,則會更新“當前白”,即將白2作爲當前白。下一輪GC將會清掃作爲“舊白”的白1標識對象

--標記清除法分爲四個階段
(1)標記階段:把根節點的集合(由lua語言可以直接訪問的對象組成)標記爲活躍狀態,在lua語言中,這個集合值包括註冊表,保存在一個活躍對象中的對象是程序可達的,因此也會被標記爲活躍(弱引用表中的內容除外)當所有可達對象都被標記爲活躍後
標記階段結束。
(2)清理階段:首先,lua語言會遍歷所有的被標記爲需要進行析構,但是又沒有被標記爲活躍狀態的對象。這些沒有被標記爲活躍狀態的對象會被標記爲活躍(復甦),並且被放在一個單獨的列表中,這個列表會在析構階段用到,然後,lua語言遍歷弱引用表並從中移除鍵或值未被標記的元素。

  • 在 Lua 5.0 以前,Lua 使用的是一個非常簡單的標記掃描算法。它從根集開始遍歷對象,把能遍歷到的對象標記爲活對象;然後再遍歷通過分配器分配出來的對象全集鏈表,把沒有標記爲活對象的其它對象都刪除。

    但是,Lua 5.0 支持 userdata ,它可以有 __gc 方法,當 userdata 被回收時,會調用這個方法。所以,一遍標記是不夠的,不能簡單的把死掉的 userdata 簡單剔除,那樣就無法正確的調用 __gc 了。所以標記流程需要分兩個階段做,第一階段把包括 userdata 在內的死對象剔除出去,然後在死對象中找回有 __gc 方法的,對它們再做一次標記復活相關的對象,這樣才能保證 userdata 的 __gc 可以正確運行。執行完 __gc 的 userdata 最終會在下一輪 gc 中釋放(如果沒有在 __gc 中復活)。 userdata 有一個單向標記,標記 __gc 方法是否有運行過,這可以保證 userdata 的 __gc 只會執行一次,即使在 __gc 中復活(重新被根集引用),也不會再次分離出來反覆運行 finalizer(終結器) 。也就是說,運行過 finalizer 的 userdata 就永久變成了一個沒有 finalizer 的 userdata 了。

(3)清除階段:遍歷所有的對象(lua會把所有創建的對象放在一個鏈表中),如果一個對象沒有被標記爲活躍狀態,就將其回收,否則,就清理標記,然後準備進入下一個清理週期。
(4)析構階段:調用清理階段被分離出來的對象的析構器(我的理解就是拿到清理階段的列表,清理列表中的對象,列表只包含真正需要清理的對象,那些在弱引用表中被標記的元素已經從列表中移除,也就是有__gc 方法的對象)。

發佈了31 篇原創文章 · 獲贊 6 · 訪問量 2818
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章