溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

PHP7垃圾回收機制的案例分析

發(fā)布時間:2020-08-26 11:57:12 來源:億速云 閱讀:146 作者:小新 欄目:編程語言

這篇文章將為大家詳細(xì)講解有關(guān)PHP7垃圾回收機制的案例分析,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

垃圾回收:

簡稱GC。顧名思義,就是廢物重利用的意思。
說垃圾回收機制之前,先接觸一下內(nèi)存泄漏。

內(nèi)存泄漏:

某大神重口味充滿畫面感的形象解釋:

大概意思就是申請了一塊地兒拉了會兒屎,拉完后不收拾,那么那塊兒地就算是糟蹋了,地越用越少,最后一地全是屎。說到底一句,用了記得還。一定程度上說,垃圾回收機制就是用來擦屁股的。

c語言垃圾回收機制:

如果用過C語言,那么申請內(nèi)存的方式是malloc或者是calloc,然后你用完這個內(nèi)存后,一定不要忘了用free函數(shù)去釋放掉,這就是手動垃圾回收,一般都是大神用這種方式。

php的自動垃圾回收機制是怎樣的呢?

這個問題我們先這么想,我們都知道php是C語言實現(xiàn)的。你想想如何用C語言實現(xiàn)對一個變量的統(tǒng)計以及釋放。C語言是如何實現(xiàn)一個變量,從聲明開始到最后沒人用了,就把這個變量所占的內(nèi)存給釋放掉(被垃圾回收)。

PHP進行內(nèi)存管理的核心算法一共兩項:
一是引用計數(shù),二是寫時拷貝

聲明一個PHP變量的時候,C語言就在底層生成一個叫做zval的struct(結(jié)構(gòu)體),如下:

zval {
string "a" //變量的名字是a
value zend_value //變量的值,聯(lián)合體
type string //變量是字符串類型
}

zval struct結(jié)構(gòu)體

(1)保存php $a的變量名
(2)php $a的變量類型
(3)php變量$a的zend_value聯(lián)合體

如果給變量賦值了,比如“hello world”,那么C語言就在底層再生成一個叫做zend_value的union(聯(lián)合體)

zend_value {
string "hello world" //值的內(nèi)容
refcount 1 //引用計數(shù)
}

zend_value的union(聯(lián)合體)

(1)保存php $a的變量的值hello world
(2)記錄php $a變量引用次數(shù)

看到 zval struct結(jié)構(gòu)體和zend_value,如果面試官問你php變量為什么能夠保存字符串"123"也能保存數(shù)字123,你知道該怎么回答了吧?就答出重點zval中有該變量的類型,當(dāng)是字符串123的時候,type就是string,此時value指向“123”;當(dāng)是整數(shù)123的時候,zval的type為int,value為123。

何為引用計數(shù)?

代碼實戰(zhàn)解析php變量引用計數(shù)

$a = 'hello,world';
echo xdebug_debug_zval( 'a');//refcount=1
$b = $a;
echo xdebug_debug_zval( 'a'); //$b引用$a,故變量a,refcount=2
$c = $a;
echo xdebug_debug_zval( 'a'); //$c引用$a,故變量a,refcount=3
unset( $c );
echo xdebug_debug_zval( 'a');//刪除了$c的引用,故變量a,refcount=2

運行結(jié)果:

a:
(refcount=1, is_ref=0)string 'hello,world' (length=11)
a:
(refcount=2, is_ref=0)string 'hello,world' (length=11)
a:
(refcount=3, is_ref=0)string 'hello,world' (length=11)
a:
(refcount=2, is_ref=0)string 'hello,world' (length=11)

何為拷貝復(fù)制?

$a = 'hello';
$b = $a;//$a賦值給$b的時候,$a的值并沒有真的復(fù)制了一份
echo xdebug_debug_zval( 'a');//$a的引用計數(shù)為2
$a = 'world';//當(dāng)我們修改$a的值為123的時候,這個時候就不得已進行復(fù)制,避免$b的值和$a的一樣
echo xdebug_debug_zval( 'a');///$a的引用計數(shù)為1

運行結(jié)果:

a:
(refcount=2, is_ref=0)string 'hello' (length=5)
a:
(refcount=1, is_ref=0)string 'world' (length=5)

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

通過簡單的案例解釋清楚了兩個要點:引用計數(shù)和寫時拷貝。

垃圾回收機制:

當(dāng)一個zval在被unset的時候、或者從一個函數(shù)中運行完畢出來(就是局部變量)的時候等等很多地方,都會產(chǎn)生zval與zend_value發(fā)生斷開的行為,這個時候zend引擎需要檢測的就是zend_value的refcount是否為0,如果為0,則直接KO free空出內(nèi)容來。如果zend_value的recount不為0,這個value不能被釋放,但是也不代表這個zend_value是清白的,因為此zend_value依然可能是個垃圾。

(1)當(dāng)php變量$a的refcount=0時,變量$a就會被垃圾回收
(2)當(dāng)php變量$a的refcount>0時,變量$a但也可能被認(rèn)為是垃圾

什么樣的情況會導(dǎo)致zend_value的refcount不為0,但是這個zend_value卻是個垃圾呢?

$arr = [ 1 ];
$arr[] = &$arr;
unset( $arr );

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

垃圾回收發(fā)生在什么時候?

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

(1)fpm運行完畢后,最后一定會gc的
(2)運行過程中,也會gc的,內(nèi)存都是即用即釋放的,而不是攢著非得到最后gc

GC處理完整流程圖

PHP7垃圾回收機制的案例分析

關(guān)于PHP7垃圾回收機制的案例分析就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI