自嘲)。。。。。2333,我以爲這是由於在php語言層面就幫咱們解決了內存回收的問題,但這讓我在和java大牛們吹牛逼的時候,聽到什麼內存泄露。。。。(納尼,我tmd怎麼歷來沒碰見過)一臉懵逼。php
本人小菜,若是下面所寫有什麼錯誤的地方,請大神指出,而且下文,不少都是讀書+看源碼以後本身的總結。java
在上一篇中我淺談的PHP中的基本數據容器,zend_value,zval算法
實際存儲數據的並不全是zend_value,還有一個被zend_value經過指針指向的具體的數據存儲結構體,如_zend_array,_zend_stringui
struct _zend_string { zend_refcounted_h gc; zend_ulong h; /* hash value */ size_t len; char val[1]; };
struct _zend_array { zend_refcounted_h gc; union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar nApplyCount, zend_uchar nIteratorsCount, zend_uchar consistency) } v; uint32_t flags; } u; uint32_t nTableMask; Bucket *arData; uint32_t nNumUsed; uint32_t nNumOfElements; uint32_t nTableSize; uint32_t nInternalPointer; zend_long nNextFreeElement; dtor_func_t pDestructor; };
注意每一個結構體中都有一個名爲gc的變量,這個變量其實就是 zend_refcounted_h 引用計數debug
正常的變量在生命週期完成以後的回收。指針
這種狀況也就是說當zend_value中refCount==0的時候,這時候屬於正常的內存回收。
垃圾回收
所謂垃圾: 就是指經過循環引用(本身引用本身,目前只在array,object類型中有出現)的形式而致使refcount永遠不爲0。這種狀況下,若是不處理,可是這些內存沒法釋放,到時內存泄露code
代碼以下:生命週期
$a = array(1,2 ); xdebug_debug_zval("a"); $a[] = &$a; xdebug_debug_zval("a"); $b=$a; unset($a); xdebug_debug_zval("a"); xdebug_debug_zval("b");
結果以下圖片
a: (refcount=1, is_ref=0) array (size=2) 0 => (refcount=0, is_ref=0)int 1 1 => (refcount=0, is_ref=0)int 2 a: (refcount=2, is_ref=1) array (size=3) 0 => (refcount=0, is_ref=0)int 1 1 => (refcount=0, is_ref=0)int 2 2 => (refcount=2, is_ref=1) &array< a: (refcount=0, is_ref=0)*uninitialized* b: (refcount=2, is_ref=0) array (size=3) 0 => (refcount=0, is_ref=0)int 1 1 => (refcount=0, is_ref=0)int 2 2 => (refcount=1, is_ref=1) &array<
按照正常邏輯,當unset($a),以後,其對應的zeng_value的refCount=1,is_ref=0,可是實際上它倒是等於refCount=2,is_ref=0, 這就致使,就算是unset($b),以後,refCount=1,is_ref=0,這種結果,像上面當refCount=0後正常回收內存。內存
![圖片上傳中...]
這就是 垃圾。
當出現垃圾以後,php的zend引擎有對應的垃圾回收機制。
其實這種機制的原理就是 :
(1).將這些垃圾放入buffer中。 (2).當buffer到達必定數量以後,啓動對全部垃圾的value自身的refCount-1,並將zend_refcounted中的gc_info變量置爲GC_GRAY
typedef struct _zend_refcounted_h { uint32_t refcount; /* reference counter 32-bit */ union { struct { ZEND_ENDIAN_LOHI_3( zend_uchar type, zend_uchar flags, /* used for strings & objects */ uint16_t gc_info) /* 就是這個變量 keeps GC root number (or 0) and color */ } v uint32_t type_info; } u; } zend_refcounted_h;
(3).遍歷當前buffer,當refCount=0是則表示當前的這個value的確是個垃圾,則將zend_refcounted中的gc_info變量置爲GC_WHITE。而後由於全部的value中都減1,因此再次遍歷,將那些減一後refcount !=0 的value+1,而後將zend_refcounted中的gc_info變量置爲GC_BLACK (4).最後遍歷buffer,將buffer中的value的zend_refcounted中的gc_info爲GC_WHITE刪除 講完GC垃圾回收算法的原理,貌似我尚未講在何時會觸發將這個**可能**的垃圾放入buffer。。。。。。 ***觸發這個機制的時機是每次出現refCount減小時候。***