佳木斯湛栽影视文化发展公司

主頁(yè) > 知識(shí)庫(kù) > PHP垃圾回收機(jī)制講解

PHP垃圾回收機(jī)制講解

熱門標(biāo)簽:科大訊飛語(yǔ)音識(shí)別系統(tǒng) Mysql連接數(shù)設(shè)置 服務(wù)器配置 阿里云 Linux服務(wù)器 銀行業(yè)務(wù) 團(tuán)購(gòu)網(wǎng)站 電子圍欄

PHP的垃圾回收機(jī)制

垃圾回收機(jī)制是一種動(dòng)態(tài)存儲(chǔ)分配的方案。它會(huì)自動(dòng)釋放程序不再需要的已分配的內(nèi)存塊。垃圾回收機(jī)制可以讓程序員不必過(guò)分關(guān)心程序內(nèi)存分配,從而將更多的精力投入到業(yè)務(wù)邏輯。在現(xiàn)在的流行各種語(yǔ)言當(dāng)中,垃圾回收機(jī)制是新一代語(yǔ)言所共有的特征,如Python、PHP、C#、Ruby等都使用了垃圾回收機(jī)制。

好了,進(jìn)入代碼實(shí)戰(zhàn)階段,注意兩點(diǎn):

$a = 'hello'. mt_rand( 1, 1000 ); 
echo xdebug_debug_zval( 'a');
$b = $a;
echo xdebug_debug_zval( 'a');
$c = $a;
echo xdebug_debug_zval( 'a');
unset( $c );
echo xdebug_debug_zval( 'a');

輸出的結(jié)果是:

其中,zval struct結(jié)構(gòu)體用于保存$a,zend_value union聯(lián)合體用于保存數(shù)據(jù)內(nèi)容也就是'hello916'。由于后面又聲明了b和c,所以C不得不又在底層給你搞出兩個(gè)zval struct結(jié)構(gòu)體來(lái)。

其中,zval和zend value的結(jié)構(gòu)大概如下:(注意?。。∵@并不是完整正確的PHP zval和zend_value在C語(yǔ)言中struct和union實(shí)現(xiàn),僅僅是挑出最重點(diǎn)的部分寫出來(lái),強(qiáng)調(diào)一下:你沒(méi)有必要一個(gè)字不差背誦過(guò)zval和zend_value,你只需要知道原理)

zval {

string "a" //變量的名字是a

value zend_value //變量的值

type string //變量是字符串類型

}

zend_value {

string "hello916" //值的內(nèi)容

refcount 1 //引用計(jì)數(shù)

}

看到上面兩個(gè),如果面試官問(wèn)你php變量為什么能夠保存字符串"123"也能保存數(shù)字123,你知道該怎么回答了吧?就答出重點(diǎn)zval中有該變量的類型,當(dāng)是字符串123的時(shí)候,type就是string,此時(shí)value指向“123”;當(dāng)是整數(shù)123的時(shí)候,zval的type為int,value為123。這就是答題的思想,這很重要!而且,通過(guò)C語(yǔ)言都是可以實(shí)現(xiàn)的!具體真正的val和zend_value的模樣,有興趣的同學(xué)可以去網(wǎng)上搜搜,如果你沒(méi)有C語(yǔ)言的底子,可能比較吃力!前者是一個(gè)struct結(jié)構(gòu)體,后者是一個(gè)union聯(lián)合體!

這個(gè)refcount就是傳說(shuō)中的引用計(jì)數(shù)了,初始化的時(shí)候a后面的引用次數(shù)為1(注意,正確說(shuō)法應(yīng)該是a后面的賦值的數(shù)組zend_value引用計(jì)數(shù)為1,而不是a這個(gè)變量zval本身)。然后我們將$b = $a,其實(shí)相當(dāng)于又一個(gè)變量指向了這個(gè)zend_value,所以refcount變?yōu)?,最后將$c = $a,同理,zend_value的refcount再次加1變成了3。然后,我們用unset( $c ),這會(huì)兒,C語(yǔ)言要做的就是把$c的zval給KO free掉,但是并不是free zend_value,這會(huì)兒zend_value的refcount就自然而然減1變成2了。

那么寫時(shí)拷貝是什么意思呢?看下面代碼:

?php
// 先不要問(wèn)為什么非要加mt_rand,不然,絕筆說(shuō)不過(guò)來(lái)了,到處都是坑
$a = 'hello'. mt_rand( 1, 1000 );
$b = $a;
$a = 123;
echo $b. PHP_EOL;
// 運(yùn)行結(jié)果,不用我說(shuō)吧,腳趾頭都知道是'hello'.mt_rand( 1, 1000 )的結(jié)果,絕對(duì)不可能是123。

其實(shí),當(dāng)你把$a賦值給$b的時(shí)候,$a的值并沒(méi)有真的復(fù)制了一份,這樣是對(duì)內(nèi)存的極度不尊重,也是對(duì)時(shí)間復(fù)雜度的極度不尊重,計(jì)算機(jī)僅僅是將$b指向了$a的值而已,這就叫多快好省。那么,什么時(shí)候真正的發(fā)生復(fù)制呢?就是當(dāng)我們修改$a的值為123的時(shí)候,這個(gè)時(shí)候就不得已進(jìn)行復(fù)制,避免$b的值和$a的一樣。

?php
$a = 'hello'. mt_rand( 1, 1000 );
$b = $a;
echo xdebug_debug_zval( 'a');
$a = 'world'. mt_rand( 2, 2000 );
echo xdebug_debug_zval( 'a');
// 運(yùn)行結(jié)果為1,其中的原理你自己應(yīng)該能理順了昂

叨逼叨了這么長(zhǎng),通過(guò)簡(jiǎn)單的案例解釋清楚了兩個(gè)要點(diǎn):引用計(jì)數(shù)和寫時(shí)拷貝,那么垃圾回收也該來(lái)了。當(dāng)一個(gè)zval在被unset的時(shí)候、或者從一個(gè)函數(shù)中運(yùn)行完畢出來(lái)(就是局部變量)的時(shí)候等等很多地方,都會(huì)產(chǎn)生zval與zend_value發(fā)生斷開的行為,這個(gè)時(shí)候zend引擎需要檢測(cè)的就是zend_value的refcount是否為0,如果為0,則直接KO free空出內(nèi)容來(lái)。如果zend_value的recount不為0(廢話一定是大于0),這個(gè)value不能被釋放,但是也不代表這個(gè)zend_value是清白的,因?yàn)榇藌end_value依然可能是個(gè)垃圾。

什么樣的情況會(huì)導(dǎo)致zend_value的refcount不為0,但是這個(gè)zend_value卻是個(gè)垃圾呢?PHP7種兩種情況:

?php
$arr = [ 1 ];
$arr[] = $arr;
unset( $arr );

這種情況下,zend_value不會(huì)能釋放,但也不能放過(guò)它,不然一定會(huì)產(chǎn)生內(nèi)存泄漏,所以這會(huì)兒zend_value會(huì)被扔到一個(gè)叫做垃圾回收堆中,然后zend引擎會(huì)依次對(duì)垃圾回收堆中的這些zend_value進(jìn)行二次檢測(cè),檢測(cè)是不是由于上述兩種情況造成的refcount為1但是自身卻確實(shí)沒(méi)有人再用了,如果一旦確定是上述兩種情況造成的,那么就會(huì)將zend_value徹底抹掉釋放內(nèi)存。

那么垃圾回收發(fā)生在什么時(shí)候?有些同學(xué)可能有疑問(wèn),就是php不是運(yùn)行一次就銷毀了嗎,我要著gc有何用?并不是啦,首先當(dāng)一次fpm運(yùn)行完畢后,最后一定還有g(shù)c的,這個(gè)銷毀就是gc;其次是,內(nèi)存都是即用即釋放的,而不是攢著非得到最后,你想想一個(gè)典型的場(chǎng)景,你的控制器里的某個(gè)方法里用了一個(gè)函數(shù),函數(shù)需要一個(gè)巨大的數(shù)組參數(shù),然后函數(shù)還需要修改這個(gè)巨大的數(shù)組參數(shù),你們應(yīng)該是函數(shù)的運(yùn)行范圍里面修改這個(gè)數(shù)組,所以此時(shí)會(huì)發(fā)生寫時(shí)拷貝了,當(dāng)函數(shù)運(yùn)行完畢后,就得趕緊釋放掉這塊兒內(nèi)存以供給其他進(jìn)程使用,而不是非得等到本地fpm request徹底完成后才銷毀。

說(shuō)到最后,說(shuō)些自己的話:大多數(shù)情況下,面試官問(wèn)你問(wèn)題主要是想一是要你個(gè)思維思路,二是看你學(xué)習(xí)程度。就像gc這個(gè)問(wèn)題,其實(shí)很多腳本語(yǔ)言的垃圾回收機(jī)制基本上都是靠引用計(jì)數(shù)和寫時(shí)拷貝這兩種算法結(jié)合完成的,所以如果你設(shè)計(jì)一門腳本語(yǔ)言,gc機(jī)制就按照這兩種算法進(jìn)行設(shè)計(jì)即可。其次是大多數(shù)phper不會(huì)看這些東西的,面試官問(wèn)你這個(gè)問(wèn)題不是要你死記硬背那么多細(xì)節(jié),你背不過(guò)的,他還是想探測(cè)你平時(shí)有沒(méi)有更積極地往深層發(fā)展的心態(tài)。

注重體現(xiàn)重點(diǎn),很多細(xì)節(jié)實(shí)在沒(méi)法寫,比如我舉個(gè)例子$a=[],xdebug_debug_zval( $a )的refcount值你猜是多少? 7.1.17下竟然是2,你是不是以為是1,然而并不是。不過(guò)你不用糾結(jié)這些細(xì)節(jié),gc的關(guān)鍵就是能說(shuō)出引用計(jì)數(shù)的原理和寫時(shí)拷貝,很多細(xì)節(jié)深處都各種奇奇怪怪的東西,面試官自己都不一定知道。

到此這篇關(guān)于PHP垃圾回收機(jī)制講解的文章就介紹到這了,更多相關(guān)PHP垃圾回收機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • 解決Laravel5.x的php artisan migrate數(shù)據(jù)庫(kù)遷移創(chuàng)建操作報(bào)錯(cuò)SQLSTATE[42000]
  • laravel執(zhí)行php artisan migrate報(bào)錯(cuò)的解決方法
  • laravel 5.1下php artisan migrate的使用注意事項(xiàng)總結(jié)
  • PHP反射機(jī)制案例講解
  • php之性能優(yōu)化案例
  • php artisan命令信息列舉

標(biāo)簽:廣元 蚌埠 大理 衡水 萍鄉(xiāng) 衢州 江蘇 棗莊

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《PHP垃圾回收機(jī)制講解》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    安陆市| 南阳市| 基隆市| 赤城县| 固原市| 宁安市| 通辽市| 金寨县| 遂溪县| 兴海县| 共和县| 启东市| 甘洛县| 仪陇县| 达拉特旗| 云安县| 翁牛特旗| 宿松县| 中西区| 湘潭市| 绩溪县| 盐亭县| 青铜峡市| 团风县| 民权县| 凤山市| 洪泽县| 马鞍山市| 吉林省| 宜丰县| 望江县| 泰顺县| 桃江县| 宜川县| 泰宁县| 亳州市| 滨州市| 慈利县| 怀宁县| 延寿县| 紫阳县|