1. 程式人生 > >理解PHP的垃圾回收機制

理解PHP的垃圾回收機制

什麽是 一次 內存空間 判斷 data- 引用 還在 而已 -i

什麽是垃圾回收機制

使用的是“引用計數”方式進行回收。簡單地理解的話,就是每個分配的內存區域都有一個計數器,記錄有多少個變量指針指向這片內存。當指向該片內存的指針數量為0,那麽該片內存區域就可以被回收。

什麽又算垃圾

首先我們需要定義一下“垃圾”的概念, 說簡單點是指變量的容器zval還存在,但是又沒有任何變量名指向此zval。因此判斷是否為垃圾的一個重要標準是有沒有變量名指向變量容器zval。。

假設我們有一段PHP代碼,使用了一個臨時變量$tmp存儲了一個字符串,在處理完字符串之後,就不需要這個$tmp變量了,$tmp變量對於我們來說可以算是一個“垃圾”了,但是$tmp其實並不是一個垃圾,

$tmp變量對我們沒有意義,但是這個變量實際還存在,$tmp符號依然指向它所對應的zval,PHP代碼中可能還會使用到此變量,所以不會將其定義為垃圾。

那麽如果我們在PHP代碼中使用完$tmp後,調用unset刪除這個變量,那麽$tmp是不是就成為一個垃圾了呢?先不著急回答這個問題。我們來舉個例子:

<?php
$a = array(one); 
$a[] = &$a; 
unset($a);
?> 

這樣$a數組就有兩個元素,一個索引為0,值為字符one,另外一個索引為1,為$a自身的引用,內部存儲如下:

a: (refcount=2, is_ref=1
)=array ( 0 => (refcount=1, is_ref=0)=one, 1 => (refcount=2, is_ref=1)=… )

“…”表示1指向a自身,是一個環形引用:

技術分享圖片

最後我們對$a進行unset,那麽$a會從符號表中刪除,同時$a指向的zval的refcount減少1

那麽問題也就產生了,$a已經不在符號表中了,用戶無法再訪問此變量,但是$a之前指向的zval的refcount變為1而不是0,因此不能被回收,這樣產生了內存泄露:

技術分享圖片

這樣,這麽一個zval就成為了一個真是意義的垃圾了,新的GC要做的工作就是清理這種垃圾。

為解決這種垃圾,產生了新的GC

在PHP5.3版本中,使用了專門GC機制清理垃圾,在之前的版本中是沒有專門的GC,那麽垃圾產生的時候,沒有辦法清理,內存就白白浪費掉了。在PHP5.3源代碼中多了以下文件:{PHPSRC}/Zend/zend_gc.h {PHPSRC}/Zend/zend_gc.c, 這裏就是新的GC的實現,我們先簡單的介紹一下算法思路,然後再從源碼的角度詳細介紹引擎中如何實現這個算法的。

新的GC算法

在較新的PHP手冊中有簡單的介紹新的GC使用的垃圾清理算法,這個算法名為 Concurrent Cycle Collection in Reference Counted Systems , 這裏不詳細介紹此算法,根據手冊中的內容來先簡單的介紹一下思路:

首先我們有幾個基本的準則:

1:如果一個zval的refcount增加,那麽此zval還在使用,不屬於垃圾

2:如果一個zval的refcount減少到0, 那麽zval可以被釋放掉,不屬於垃圾

3:如果一個zval的refcount減少之後大於0,那麽此zval還不能被釋放,此zval可能成為一個垃圾

只有在準則3下,GC才會把zval收集起來,然後通過新的算法來判斷此zval是否為垃圾。那麽如何判斷這麽一個變量是否為真正的垃圾呢?

簡單的說,就是對此zval中的每個元素進行一次refcount減1操作,操作完成之後,如果zval的refcount=0,那麽這個zval就是一個垃圾。

PHP5.3針對這個重大的缺陷做了優化。雖然其基礎仍然是引用計數,但是在做了一些改良,能夠將環狀引用導致的內存泄露控制在一定的規模以內。當然,這並不是說你可以隨便濫用內存,編寫代碼時仍然要小心為上!
補充重點:
1.PHP腳本運行完畢,該腳本申請的所有內存空間都會釋放,不管是否存在環狀引用。因此環狀引用內存泄露的問題一般只影響長時間運行的程序腳本。

2.垃圾回收機制需要滿足一定的條件才會執行。因此unset後,系統並不一定會立即回收垃圾。

3.unset的作用。
“unset只是斷開一個變量到一塊內存區域的連接,同時將該內存區域的引用計數-1”。也就是說,如果有一個以上的變量指向同一個內存區域,或者存在環狀引用,那麽unset不會使內存區域釋放。斷開也說明unset並不會直接刪除內存區域,而只是改變其引用計數而已。

4.$a=null的作用。
“$a = null 是直接將$a 指向的數據結構置空,同時將其引用計數歸0”。根據我對這個定義的理解,=null操作可以立即釋放掉內存空間!

因此很多PHP技巧中不厭其煩地對我們說,先將變量設為null,再unset。理解其深層原理後,我才徹底理解了這樣做的原因!=null才是根本!

理解PHP的垃圾回收機制