PHP GC回收機(jī)制實例詳解
前言
GC的全稱是Garbage Collection也就是垃圾回收的意思,在PHP中,是使用引用計數(shù)和回收周期來自動管理內(nèi)存對象的,當(dāng)一個對象被設(shè)置為NULL,或者沒有任何指針指向時,他就會變成垃圾,被GC機(jī)制回收掉。
環(huán)境配置
php.ini終配置好xdebug,xdebug_debug_zval
是用來查看容器變量內(nèi)容的函數(shù)
<?php $a = "F12"; xdebug_debug_zval("a"); ?>
在PHP GC機(jī)制中,當(dāng)程序終止時就會讓變量的refcount
減1,如果refcount-1
為0的話,就會銷毀回收該變量
引用計數(shù)
is_ref
表示該變量是否被引用,操作系統(tǒng)學(xué)的好的同學(xué)應(yīng)該很容易理解該內(nèi)容
<?php $a = "F12"; $b = &$a; xdebug_debug_zval("a"); ?> # 運(yùn)行結(jié)果 a: (refcount=2, is_ref=1)='F12'
$b是$a的引用,所以is_ref=1
,同時refcount
也會加1,因為此時是有兩個變量的(兩變量指向同一個地址),所以銷毀時要讓refcount
減2。
當(dāng)變量是array類型時,也是一樣的規(guī)則
<?php $a = "F12"; $arr = array(0=>"test", 1=>&$a); xdebug_debug_zval("arr"); ?> # 運(yùn)行結(jié)果 arr: (refcount=1, is_ref=0)=array (0 => (refcount=1, is_ref=0)='test', 1 => (refcount=2, is_ref=1)='F12')
如果我們在引用前將$a銷毀會發(fā)生什么?
<?php $a = "F12"; unset($a); $arr = array(0=>"test", 1=>&$a); xdebug_debug_zval("a"); xdebug_debug_zval("arr"); ?> # 運(yùn)行結(jié)果 a: (refcount=2, is_ref=1)=NULL arr: (refcount=1, is_ref=0)=array (0 => (refcount=1, is_ref=0)='test', 1 => (refcount=2, is_ref=1)=NULL)
<?php $a = "F12"; $arr = array(0=>"test", 1=>&$a); unset($a); xdebug_debug_zval("a"); xdebug_debug_zval("arr"); ?> # 運(yùn)行結(jié)果 a: no such symbol arr: (refcount=1, is_ref=0)=array (0 => (refcount=1, is_ref=0)='test', 1 => (refcount=1, is_ref=1)='F12')
第一種情況,$a沒有被銷毀,因為在之后又引用了$a,所以$a只是指向了一個NULL,第二種情況就把$a銷毀了
PHP GC在反序列化中的使用
一個簡單的demo
<?php class gc{ public $num; public function __construct($num) { $this->num=$num; echo "construct(".$num.")"."\n"; } public function __destruct() { echo "destruct(".$this->num.")"."\n"; } } $a=new gc(1); $b=new gc(2); $c=new gc(3); # 運(yùn)行結(jié)果 construct(1) construct(2) construct(3) destruct(3) destruct(2) destruct(1)
先創(chuàng)建的對象最后銷毀,看看變量的內(nèi)容情況:
可以看到refcount
為1,所以當(dāng)程序結(jié)束時,減1就會被回收
如果我們不把new的gc對象賦值給$a會怎樣?
<?php class gc{ public $num; public function __construct($num) { $this->num=$num; echo "construct(".$num.")"."\n"; } public function __destruct() { echo "destruct(".$this->num.")"."\n"; } } new gc(1); $b=new gc(2); $c=new gc(3); # 運(yùn)行結(jié)果 construct(1) destruct(1) construct(2) construct(3) destruct(3) destruct(2)
可以看到第一個gc對象,創(chuàng)建完就被回收了,因為沒被其它變量引用,它的refcount
一開始就是0,所以直接被回收
繞過Exception異常
思路一
一個簡單的demo:
<?php class gc{ public $num; public function __construct($num) { $this->num=$num; } public function __destruct() { echo "Hello World!"; } } $a = new gc(1); $ser = serialize($a); $b = unserialize($ser); throw new Exception("F12 is bad");
正常來說會輸出一個Hello World!
,但是因為觸發(fā)了異常,所以對象并沒有被回收
我們修改一下代碼:
<?php class gc{ public $num; public function __construct($num) { $this->num=$num; } public function __destruct() { echo "Hello World!"; } } $a = array(0=>new gc(1),1=>1); $ser = serialize($a); echo $ser; $ser = 'a:2:{i:0;O:2:"gc":1:{s:3:"num";i:1;}i:0;i:1;}'; $b = unserialize($ser); throw new Exception("F12 is bad");
這里我們我們修改序列化的內(nèi)容,將$a[0]隨便指向誰,從而使new的gc對象沒有引用的變量,所以觸發(fā)提前回收,跟上面舉的直接new gc,并不賦值是一個道理
思路二
這種方法更加簡單粗暴,我們只需要讓序列化的數(shù)據(jù)出錯,那么當(dāng)反序列化時出錯時,也會讓該對象提前回收
<?php class gc{ public $num; public function __construct($num) { $this->num=$num; } public function __destruct() { echo "Hello World!"; } } $a = new gc(1); $ser = serialize($a); echo $ser; $ser = 'O:2:"gc":1:{s:3:"num";i:1;'; $b = unserialize($ser); throw new Exception("F12 is bad");
這里我們刪去一個}
,依然輸出了Hello World!
到此這篇關(guān)于PHP GC回收機(jī)制詳解 的文章就介紹到這了,更多相關(guān)PHP GC回收機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
php使用FFmpeg接口獲取視頻的播放時長、碼率、縮略圖以及創(chuàng)建時間
本篇文章主要介紹了php使用FFmpeg接口獲取視頻的播放時長、碼率、縮略圖以及創(chuàng)建時間,具有一定的參考價值,有需要的可以了解一下。2016-11-11Thinkphp中volist標(biāo)簽mod控制一定記錄的換行BUG解決方法
這篇文章主要介紹了Thinkphp中volist標(biāo)簽mod控制一定記錄的換行BUG解決方法,涉及針對標(biāo)簽執(zhí)行語句順序的修改,非常具有實用價值,需要的朋友可以參考下2014-11-11laravel 判斷查詢數(shù)據(jù)庫返回值的例子
今天小編就為大家分享一篇laravel 判斷查詢數(shù)據(jù)庫返回值的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-10-10