原文:回收週期(Collecting Cycles) ,增長一篇論文在底部。php
如下過程僅對數組和對象類型起做用。算法
傳統上,像之前的 php 用到的引用計數內存機制,沒法處理循環的引用內存泄漏。然而 5.3.0 PHP 使用文章» 引用計數系統中的同步週期回收(Concurrent Cycle Collection in Reference Counted Systems)中的同步算法,來處理這個內存泄漏問題。數組
對算法的徹底說明有點超出這部份內容的範圍,將只介紹其中基礎部分。首先,咱們先要創建一些基本規則,若是一個引用計數增長,它將繼續被使用,固然就再也不在垃圾中。若是引用計數減小到零,所在變量容器將被清除(free)。就是說,僅僅在引用計數減小到非零值時,纔會產生垃圾週期(garbage cycle)。其次,在一個垃圾週期中,經過檢查引用計數是否減1,而且檢查哪些變量容器的引用次數是零,來發現哪部分是垃圾。緩存
爲避免不得不檢查全部引用計數可能減小的垃圾週期,這個算法把全部可能根(possible roots 都是zval變量容器),放在根緩衝區(root buffer)中(用紫色來標記,稱爲疑似垃圾),這樣能夠同時確保每一個可能的垃圾根(possible garbage root)在緩衝區中只出現一次。僅僅在根緩衝區滿了時,纔對緩衝區內部全部不一樣的變量容器執行垃圾回收操做。看上圖的步驟 A。函數
在步驟 B 中,模擬刪除每一個紫色變量。模擬刪除時可能將不是紫色的普通變量引用數減」1″,若是某個普通變量引用計數變成0了,就對這個普通變量再作一次模擬刪除。每一個變量只能被模擬刪除一次,模擬刪除後標記爲灰(原文說確保不會對同一個變量容器減兩次」1″,不對的吧)。spa
在步驟 C 中,模擬恢復每一個紫色變量。恢復是有條件的,當變量的引用計數大於0時纔對其作模擬恢復。一樣每一個變量只能恢復一次,恢復後標記爲黑,基本就是步驟 B 的逆運算。這樣剩下的一堆沒能恢復的就是該刪除的藍色節點了,在步驟 D 中遍歷出來真的刪除掉。對象
算法中都是模擬刪除、模擬恢復、真的刪除,都使用簡單的遍歷便可(最典型的深搜遍歷)。複雜度爲執行模擬操做的節點數正相關,不僅是紫色的那些疑似垃圾變量。圖片
如今,你已經對這個算法有了基本瞭解,咱們回頭來看這個如何與PHP集成。默認的,PHP的垃圾回收機制是打開的,而後有個 php.ini 設置容許你修改它:zend.enable_gc 。內存
當垃圾回收機制打開時,每當根緩存區存滿時,就會執行上面描述的循環查找算法。根緩存區有固定的大小,可存10,000個可能根,固然你能夠經過修改PHP源碼文件Zend/zend_gc.c中的常量GC_ROOT_BUFFER_MAX_ENTRIES,而後從新編譯PHP,來修改這個10,000值。當垃圾回收機制關閉時,循環查找算法永不執行,然而,可能根將一直存在根緩衝區中,無論在配置中垃圾回收機制是否激活。get
當垃圾回收機制關閉時,若是根緩衝區存滿了可能根,更多的可能根顯然不會被記錄。那些沒被記錄的可能根,將不會被這個算法來分析處理。若是他們是循環引用週期的一部分,將永不能被清除進而致使內存泄漏。
即便在垃圾回收機制不可用時,可能根也被記錄的緣由是,相對於每次找到可能根後檢查垃圾回收機制是否打開而言,記錄可能根的操做更快。不過垃圾回收和分析機制自己要耗很多時間。
除了修改配置zend.enable_gc ,也能經過分別調用gc_enable() 和 gc_disable()函數來打開和關閉垃圾回收機制。調用這些函數,與修改配置項來打開或關閉垃圾回收機制的效果是同樣的。即便在可能根緩衝區還沒滿時,也能強制執行週期回收。你能調用gc_collect_cycles()函數達到這個目的。這個函數將返回使用這個算法回收的週期數。
容許打開和關閉垃圾回收機制而且容許自主的初始化的緣由,是因爲你的應用程序的某部分多是高時效性的。在這種狀況下,你可能不想使用垃圾回收機制。固然,對你的應用程序的某部分關閉垃圾回收機制,是在冒着可能內存泄漏的風險,由於一些可能根也許存不進有限的根緩衝區。所以,就在你調用gc_disable()函數釋放內存以前,先調用gc_collect_cycles()函數可能比較明智。由於這將清除已存放在根緩衝區中的全部可能根,而後在垃圾回收機制被關閉時,可留下空緩衝區以有更多空間存儲可能根。
引用計數系統中的同步週期回收(ConcurrentCycleCollection)