php垃圾回收機制,對於PHPer來說是一個不陌生但又不是很熟悉的內容。那麼php是怎麼實現對不需要的記憶體進行回收的呢?
php變數的記憶體儲存結構:
首先還是需要了解下基礎知識,以便於垃圾回收原理內容的理解。大家都知道php是由C寫而成的,所以php變數的內部儲存結構也會和C語言相關,也就是zval的結構體:
struct _zval_struct { union { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } value; //变量value值 zend_uint refcount__gc; //引用计数内存中使用次数,为0删除该变量 zend_uchar type; //变量类型 zend_uchar is_ref__gc; //区分是否是引用变量 };
從上面結構體內容可以看出每一個php變數都會由變數類型、value值、引用計數次數和是否是引用變數四部分組成
註:上面zval結構體是php5.3版本之後的結構,php5.3之前因為沒有引進新的垃圾回收機制,即GC,所以命名也沒有_gc
;而php7版本之後由於效能問題所以改寫了zval結構,這裡不再表述。
引用計數原則:
了解了php變數的內部儲存結構之後,我們再了解下php變數賦值相關的原理與早期垃圾回收機制
變數容器
非array和object變數
每次將常數賦值給一個變數時,都會產生一個變數容器。
範例:
$a = '许铮的技术成长之路'; xdebug_debug_zval('a')
結果:
a: (refcount=1, is_ref=0)='许铮的技术成长之路'
array和object變數
$b = [ 'name' => '许铮的技术成长之路', 'number' => 3 ]; xdebug_debug_zval('b')結果:
b: (refcount=1, is_ref=0)=array ('name' => (refcount=1, is_ref=0)='许铮的技术成长之路', 'number' => (refcount=1, is_ref=0)=3)
賦值原理(寫時複製技術)
了解了常數賦值之後,接下來我們從記憶體角度思考變數之間的賦值舉例:$a = [ 'name' => '许铮的技术成长之路', 'number' => 3 ]; //创建一个变量容器,变量a指向给变量容器,a的ref_count为1 $b = $a; //变量b也指向变量a指向的变量容器,a和b的ref_count为2 xdebug_debug_zval('a', 'b'); $b['name'] = '许铮的技术成长之路1';//变量b的其中一个元素发生改变,此时会复制出一个新的变量容器, 变量b重新指向新的变量容器,a和b的ref_count变成1 xdebug_debug_zval('a', 'b');結果:
a: (refcount=2, is_ref=0)=array ('name' => (refcount=1, is_ref=0)='许铮的技术成长之路', 'number' => (refcount=1, is_ref=0)=3) b: (refcount=2, is_ref=0)=array ('name' => (refcount=1, is_ref=0)='许铮的技术成长之路', 'number' => (refcount=1, is_ref=0)=3) a: (refcount=1, is_ref=0)=array ('name' => (refcount=1, is_ref=0)='许铮的技术成长之路', 'number' => (refcount=1, is_ref=0)=3) b: (refcount=1, is_ref=0)=array ('name' => (refcount=1, is_ref=0)='许铮的技术成长之路1', 'number' => (refcount=1, is_ref=0)=3)所以,當變數a賦值給變數b的時候,並沒有立刻產生一個新的變數容器,而是將變數b指向了變數a指向的變數容器,也就是記憶體"共享";而當變數b其中一個元素改變時,才會真正發生變數容器複製,這就是
寫時複製技術
引用計數清除0
$a = "许铮的技术成长之路"; $b = $a; xdebug_debug_zval('a'); unset($b); xdebug_debug_zval('a');結果:
a: (refcount=2, is_ref=0)='许铮的技术成长之路' a: (refcount=1, is_ref=0)='许铮的技术成长之路'
循環引用引發的記憶體洩漏問題:
$a = array( 'one' ); $a[] = &$a; xdebug_debug_zval( 'a' );由於該範例不好輸出結果,用圖表表示,如圖: 範例:
unset($a); xdebug_debug_zval('a');#如圖:
確認為垃圾的準則:
1、如果引用計數減少到零,所在變數容器將被清除(free),不屬於垃圾2、如果一個zval 的引用計數減少後還大於0,那麼它就會進入垃圾週期。其次,在一個垃圾週期中,透過檢查引用計數是否減1,並且檢查哪些變數容器的引用次數是零,來發現哪一部分是垃圾。
總結:
垃圾回收機制:1、以php的引用計數機制為基礎(php5.3以前只有這個機制)
2 、同時使用根緩衝區機制,當php發現有存在循環引用的zval時,就會把其投入到根緩衝區,當根緩衝區達到配置文件中的指定數量後,就會進行垃圾回收,以此解決循環引用導致的記憶體洩漏問題(php5.3開始引入此機制)
以上是十分鐘搞清php垃圾回收原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!