您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關PHP中怎么實現垃圾回收機制,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
原理: 給對象添加一個引用計數器,每當有一個地方引用它,計數器的值就加一。每當有一個引用失效,計數器的值就減一。
如果一個變量 value 的 refcount 減一之后等于 0,此 value 可以被釋放
掉,不屬于
垃圾。垃圾回收器不會處理 。
如果一個變量 value 的 refcount 減一之后還是大于 0,此 value 被認為不能被釋放掉,可能
成為一個垃圾。
垃圾回收器將可能的垃圾收集起來,等達到一定數量后開始啟動垃圾鑒定程序
,把真正
的垃圾釋放掉。
缺點: 需要維護引用計數器,有一定的消耗。且較難處理循環引用的問題。后面也會講到如何解決這個問題。
下面的例子說明引用計數的是如何變化的:
每個 php 變量存在一個叫 zval
的變量容器中。一個 zval 變量容器,除了包含變量的類型和值,還包括兩個字節的額外信息。
第一個是 is_ref
,是個 bool 值,用來標識這個變量是否是屬于引用集合(reference set) 。通過這個字節,php 引擎才能把普通變量和引用變量區分開來,由于 php 允許用戶通過使用&來使用自定義引用,zval 變量容器中還有一個內部引用計數機制,來優化內存使用。
第二個額外字節是 refcount
,用以表示指向這個 zval 變量容器的變量(也稱符號即 symbol )個數。
有 5 種類型用的引用計數:
string、array、object、resource、reference
下面的表格說明了只有 type_flag 為以下 8 種類型且 IS_TYPE_REFOUNTED=true 的變量才使用引用計數,如下表所示
自動回收:在變量 zval 斷開 value 的指向時,如果發現 refcount=0 則會直接釋放 value。
修改變量時會斷開原有 value 的指向
函數返回時會釋放所有的局部變量
斷開 value 指向的情形
主動回收
調用 unset()
函數。類似于 Java 中的 System.gc()
垃圾收集器收集的可能垃圾
到達一定數量后,啟動垃圾鑒定、回收程序。
原理:垃圾是由于成員引用自身導致的,那么就對 value 的 refcount 減一操作,如果 value 的 refount 變為了 0,則表明其引用全部來自自身成員,value 屬于垃圾。另外垃圾只會出現在array、object類型中。
步驟一: 遍歷垃圾回收器的 buffer 緩沖區,把 value 標為灰色,把 value 的成員的 refount-1,還是標為灰色。
步驟二: 遍歷垃圾回收器的 buffer 緩沖區,如果 value 的 refcount 等于 0,標為白色,認為是垃圾;如果不等于 0,則表示還有外部的引用,不是垃圾,將 refcount+1 還原回去,標為黑色。
步驟三: 遍歷垃圾回收器的 buffer 緩沖區,將 value 為非白色的節點從 buffer 中刪除,最終 buffer 緩沖區中都是真正的垃圾。
步驟四: 遍歷垃圾回收器的 buffer 緩沖區,釋放此 value。
我稱 _zend_gc_globals 為垃圾管家,結構體會對垃圾進行管理,收集到的可能成為垃圾的 value 就保存在這個結構的 buf 中,稱為垃圾緩存區。
文件路徑:\Zend\zend_gc.h。
(1)php.ini 解析后調用 gc_init() 初始垃圾管家_zend_gc_globals
文件路徑:\Zend\zend_gc.c
(2)gc_init() 函數里面調用 gc_reset() 函數初始化。
(1)在銷毀一個變量時就會判斷是否需要收集。調用 i_zval_ptr_dtor() 函數
文件路徑:Zend\zend_variables.h
如果 refcount 減一后,refcount 等于 0,則認為不是垃圾,調用 _zval_dtor_func 方法釋放此 value。
如果 refcount 減一后,refcount 大于 0,則認為 value 可能是垃圾,垃圾管家進行收集
文件路徑:\Zend\zend_gc.c,調用方法:gc_possible_root
拿出 unused 指向的節點。
如果拿出的節點是可用的,則將 unused 指向下一個節點。
如果 unused 沒有可用的,且 first_unused 還沒有推進到 last_unused,則表示 buf 緩存區中還有可用的節點。
拿出 first_unused 指向的節點。
first_unused 指向下一個節點。
buf 緩存區已滿,啟動垃圾鑒定、垃圾回收。
如果未啟用垃圾回收,則直接返回。
將插入的變量標為紫色,防止重復插入。
將該節點在 buf 數組中的位置保存到了 gc_info 中,當后續 value 的 refcount 變為了 0。
需要將其從 buf 中刪除時可以知道該 value 保存在哪個 gc_root_buffer 中。
由于回收方法 zend_gc_collect_cycles() 實在是太長,我把幾個關鍵步驟理出來了:
掃描根節點
收集根節點
調用回收器
清理變量
收集完成
關于PHP中怎么實現垃圾回收機制就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。