站長資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

十分鐘搞清php垃圾回收原理

十分鐘搞清php垃圾回收原理

php垃圾回收機制,對于PHPer來說是一個不陌生但是又不是很熟悉的內(nèi)容。那么php是怎么實現(xiàn)對不需要的內(nèi)存進行回收的呢?

php變量的內(nèi)存存儲結構:

首先還是需要了解下基礎知識,便于垃圾回收原理內(nèi)容的理解。大家都知道php是由C編寫而成的,所以php變量的內(nèi)部存儲結構也會和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;   //引用計數(shù)內(nèi)存中使用次數(shù),為0刪除該變量     zend_uchar type;           //變量類型     zend_uchar is_ref__gc;    //區(qū)分是否是引用變量 };

從上面結構體內(nèi)容可以看出每一個php變量都會由變量類型、value值、引用計數(shù)次數(shù)和是否是引用變量四部分組成

注:上面zval結構體是php5.3版本之后的結構,php5.3之前因為沒有引入新的垃圾回收機制,即GC,所以命名也沒有_gc;而php7版本之后由于性能問題所以改寫了zval結構,這里不再表述。

引用計數(shù)原理

了解了php變量的內(nèi)部存儲結構之后,我們再了解下php變量賦值相關的原理和早期垃圾回收機制

變量容器

非array和object變量

每次將常量賦值給一個變量時,都會產(chǎn)生一個變量容器。

舉例:

$a = '許錚的技術成長之路'; xdebug_debug_zval('a')

結果:

a: (refcount=1, is_ref=0)='許錚的技術成長之路'

array和object變量

會產(chǎn)生元素個數(shù)+1的變量容器

舉例:

$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)

賦值原理(寫時復制技術)

了解了常量賦值之后,接下來我們從內(nèi)存角度思考變量之間的賦值

舉例:

$a = [ 'name' => '許錚的技術成長之路', 'number' => 3 ]; //創(chuàng)建一個變量容器,變量a指向給變量容器,a的ref_count為1 $b = $a; //變量b也指向變量a指向的變量容器,a和b的ref_count為2 xdebug_debug_zval('a', 'b'); $b['name'] = '許錚的技術成長之路1';//變量b的其中一個元素發(fā)生改變,此時會復制出一個新的變量容器, 變量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指向的變量容器,即內(nèi)存"共享";而當變量b其中一個元素發(fā)生改變時,才會真正發(fā)生變量容器復制,這就是寫時復制技術

引用計數(shù)清0

當變量容器的ref_count計數(shù)清0時,表示該變量容器就會被銷毀,實現(xiàn)了內(nèi)存回收,這也是php5.3版本之前的垃圾回收機制

舉例:

$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)='許錚的技術成長之路'

循環(huán)引用引發(fā)的內(nèi)存泄漏問題:

但是php5.3版本之前的垃圾回收機制存在一個漏洞,即當數(shù)組或對象內(nèi)部子元素引用其父元素,而此時如果發(fā)生了刪除其父元素的情況,此變量容器并不會被刪除,因為其子元素還在指向該變量容器,但是由于所有作用域內(nèi)都沒有指向該變量容器的符號,所以無法被清除,因此會發(fā)生內(nèi)存泄漏,直到該腳本執(zhí)行結束

舉例:

$a = array( 'one' ); $a[] = &$a; xdebug_debug_zval( 'a' );

由于該示例不好輸出結果,用圖表示,如圖:

十分鐘搞清php垃圾回收原理

舉例:

unset($a); xdebug_debug_zval('a');

如圖:

十分鐘搞清php垃圾回收原理

新的垃圾回收機制:

php5.3版本之后引入根緩沖機制,即php啟動時默認設置指定zval數(shù)量的根緩沖區(qū)(默認是10000),當php發(fā)現(xiàn)有存在循環(huán)引用的zval時,就會把其投入到根緩沖區(qū),當根緩沖區(qū)達到配置文件中的指定數(shù)量(默認是10000)后,就會進行垃圾回收,以此解決循環(huán)引用導致的內(nèi)存泄漏問題

確認為垃圾的準則

1、如果引用計數(shù)減少到零,所在變量容器將被清除(free),不屬于垃圾
2、如果一個zval 的引用計數(shù)減少后還大于0,那么它會進入垃圾周期。其次,在一個垃圾周期中,通過檢查引用計數(shù)是否減1,并且檢查哪些變量容器的引用次數(shù)是零,來發(fā)現(xiàn)哪部分是垃圾。

總結

垃圾回收機制:
1、以php的引用計數(shù)機制為基礎(php5.3以前只有該機制)
2、同時使用根緩沖區(qū)機制,當php發(fā)現(xiàn)有存在循環(huán)引用的zval時,就會把其投入到根緩沖區(qū),當根緩沖區(qū)達到配置文件中的指定數(shù)量后,就會進行垃圾回收,以此解決循環(huán)引用導致的內(nèi)存泄漏問題(php5.3開始引入該機制)

贊(0)
分享到: 更多 (0)
網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號
四虎精品成人免费视频| 国产成人无码综合亚洲日韩| 亚洲综合一区二区精品导航 | 亚洲国产精品人人做人人爽| 妇女自拍偷自拍亚洲精品| 无码国产精品一区二区免费 | 久久综合日韩亚洲精品色| 国产在线91精品入口| 国产成人99久久亚洲综合精品| 日韩中文有码高清| 中日韩一区二区三区| 国产在线麻豆精品| 国产精品66在线观看| 国产美女精品人人做人人爽| 无码人妻精品一区二区三区9厂| 青娱分类视频精品免费2| 亚洲系列国产精品制服丝袜第 | 无码国产精品一区二区高潮| 久久亚洲精品无码网站| 在线综合亚洲中文精品| 国产成人精品日本亚洲专| 亚洲国产成人精品青青草原| 2020国产精品永久在线观看| 久久亚洲日韩精品一区二区三区| 国产成人久久精品一区二区三区| 精品国产_亚洲人成在线高清| 99精品无人区乱码1区2区3区| 色噜噜亚洲精品中文字幕| 伊人久久精品午夜| 伊人久久精品午夜| 久久99热狠狠色精品一区| 亚洲国产精品无码AAA片| 久久国产精品免费专区| 久久精品九九热无码免贵| 久久九九精品国产综合喷水| 无码人妻精品一区二区三区99仓本| 久久精品国产亚洲av四虎| 久久久久无码精品亚洲日韩| 久久亚洲精品人成综合网| 91大神精品网站在线观看| 精品一区高潮喷吹在线播放|