男女疯狂一边摸一边做羞羞视频|啊好深好硬快点用力别停动态图|亚洲一区无码中文字幕|特级无码毛片免费视频播放▽|久久狠狠躁免费观看|国内精品久久久久久网站

引用計數基本知識

每個(gè)php變量存在一個(gè)叫"zval"的變量容器中。一個(gè)zval變量容器,除了包含變量的類(lèi)型和值,還包括兩個(gè)字節的額外信息。第一個(gè)是"is_ref",是個(gè)bool值,用來(lái)標識這個(gè)變量是否是屬于引用集合(reference set)。通過(guò)這個(gè)字節,php引擎才能把普通變量和引用變量區分開(kāi)來(lái),由于php允許用戶(hù)通過(guò)使用&來(lái)使用自定義引用,zval變量容器中還有一個(gè)內部引用計數機制,來(lái)優(yōu)化內存使用。第二個(gè)額外字節是"refcount",用以表示指向這個(gè)zval變量容器的變量(也稱(chēng)符號即symbol)個(gè)數。所有的符號存在一個(gè)符號表中,其中每個(gè)符號都有作用域(scope),那些主腳本(比如:通過(guò)瀏覽器請求的的腳本)和每個(gè)函數或者方法也都有作用域。

當一個(gè)變量被賦常量值時(shí),就會(huì )生成一個(gè)zval變量容器,如下例這樣:

示例 #1 生成一個(gè)新的zval容器

<?php
$a 
"new string";
?>

在上例中,新的變量a,是在當前作用域中生成的。并且生成了類(lèi)型為 string 和值為new string的變量容器。在額外的兩個(gè)字節信息中,"is_ref"被默認設置為 false,因為沒(méi)有任何自定義的引用生成。"refcount" 被設定為 1,因為這里只有一個(gè)變量使用這個(gè)變量容器. 注意到當"refcount"的值是1時(shí),"is_ref"的值總是false. 如果你已經(jīng)安裝了? Xdebug,你能通過(guò)調用函數 xdebug_debug_zval()顯示"refcount"和"is_ref"的值。

示例 #2 顯示zval信息

<?php
xdebug_debug_zval
('a');
?>

以上例程會(huì )輸出:

a: (refcount=1, is_ref=0)='new string'

把一個(gè)變量賦值給另一變量將增加引用次數(refcount).

示例 #3 增加一個(gè)zval的引用計數

<?php
$a 
"new string";
$b $a;
xdebug_debug_zval'a' );
?>

以上例程會(huì )輸出:

a: (refcount=2, is_ref=0)='new string'

這時(shí),引用次數是2,因為同一個(gè)變量容器被變量 a 和變量 b關(guān)聯(lián).當沒(méi)必要時(shí),php不會(huì )去復制已生成的變量容器。變量容器在”refcount“變成0時(shí)就被銷(xiāo)毀. 當任何關(guān)聯(lián)到某個(gè)變量容器的變量離開(kāi)它的作用域(比如:函數執行結束),或者對變量調用了函數 unset()時(shí),”refcount“就會(huì )減1,下面的例子就能說(shuō)明:

示例 #4 減少引用計數

<?php
$a 
"new string";
$c $b $a;
xdebug_debug_zval'a' );
unset( 
$b$c );
xdebug_debug_zval'a' );
?>

以上例程會(huì )輸出:

a: (refcount=3, is_ref=0)='new string'
a: (refcount=1, is_ref=0)='new string'

如果我們現在執行 unset($a);,包含類(lèi)型和值的這個(gè)變量容器就會(huì )從內存中刪除。

復合類(lèi)型(Compound Types)

當考慮像 arrayobject這樣的復合類(lèi)型時(shí),事情就稍微有點(diǎn)復雜. 與 標量(scalar)類(lèi)型的值不同,arrayobject類(lèi)型的變量把它們的成員或屬性存在自己的符號表中。這意味著(zhù)下面的例子將生成三個(gè)zval變量容器。

示例 #5 Creating a array zval

<?php
$a 
= array( 'meaning' => 'life''number' => 42 );
xdebug_debug_zval'a' );
?>

以上例程的輸出類(lèi)似于:

a: (refcount=1, is_ref=0)=array (
   'meaning' => (refcount=1, is_ref=0)='life',
   'number' => (refcount=1, is_ref=0)=42
)

圖示:

一個(gè)簡(jiǎn)單數組的zval

這三個(gè)zval變量容器是: a,meaningnumber。增加和減少”refcount”的規則和上面提到的一樣. 下面, 我們在數組中再添加一個(gè)元素,并且把它的值設為數組中已存在元素的值:

示例 #6 添加一個(gè)已經(jīng)存在的元素到數組中

<?php
$a 
= array( 'meaning' => 'life''number' => 42 );
$a['life'] = $a['meaning'];
xdebug_debug_zval'a' );
?>

以上例程的輸出類(lèi)似于:

a: (refcount=1, is_ref=0)=array (
   'meaning' => (refcount=2, is_ref=0)='life',
   'number' => (refcount=1, is_ref=0)=42,
   'life' => (refcount=2, is_ref=0)='life'
)

圖示:

帶有引用的簡(jiǎn)單數組的zval

從以上的xdebug輸出信息,我們看到原有的數組元素和新添加的數組元素關(guān)聯(lián)到同一個(gè)"refcount"2的zval變量容器. 盡管 Xdebug的輸出顯示兩個(gè)值為'life'的 zval 變量容器,其實(shí)是同一個(gè)。 函數xdebug_debug_zval()不顯示這個(gè)信息,但是你能通過(guò)顯示內存指針信息來(lái)看到。

刪除數組中的一個(gè)元素,就是類(lèi)似于從作用域中刪除一個(gè)變量. 刪除后,數組中的這個(gè)元素所在的容器的“refcount”值減少,同樣,當“refcount”為0時(shí),這個(gè)變量容器就從內存中被刪除,下面又一個(gè)例子可以說(shuō)明:

示例 #7 從數組中刪除一個(gè)元素

<?php
$a 
= array( 'meaning' => 'life''number' => 42 );
$a['life'] = $a['meaning'];
unset( 
$a['meaning'], $a['number'] );
xdebug_debug_zval'a' );
?>

以上例程的輸出類(lèi)似于:

a: (refcount=1, is_ref=0)=array (
   'life' => (refcount=1, is_ref=0)='life'
)

現在,當我們添加一個(gè)數組本身作為這個(gè)數組的元素時(shí),事情就變得有趣,下個(gè)例子將說(shuō)明這個(gè)。例中我們加入了引用操作符,否則php將生成一個(gè)復制。

示例 #8 把數組作為一個(gè)元素添加到自己

<?php
$a 
= array( 'one' );
$a[] =& $a;
xdebug_debug_zval'a' );
?>

以上例程的輸出類(lèi)似于:

a: (refcount=2, is_ref=1)=array (
   0 => (refcount=1, is_ref=0)='one',
   1 => (refcount=2, is_ref=1)=...
)

圖示:

自引用(curcular reference,自己是自己的一個(gè)元素)的數組的zval

能看到數組變量 (a) 同時(shí)也是這個(gè)數組的第二個(gè)元素(1) 指向的變量容器中“refcount”為 2。上面的輸出結果中的"..."說(shuō)明發(fā)生了遞歸操作, 顯然在這種情況下意味著(zhù)"..."指向原始數組。

跟剛剛一樣,對一個(gè)變量調用unset,將刪除這個(gè)符號,且它指向的變量容器中的引用次數也減1。所以,如果我們在執行完上面的代碼后,對變量$a調用unset, 那么變量 $a 和數組元素 "1" 所指向的變量容器的引用次數減1, 從"2"變成"1". 下例可以說(shuō)明:

示例 #9 Unsetting $a

(refcount=1, is_ref=1)=array (
   0 => (refcount=1, is_ref=0)='one',
   1 => (refcount=1, is_ref=1)=...
)

圖示:

Zvals after removal of array with a circular reference demonstrating the memory leak

清理變量容器的問(wèn)題(Cleanup Problems)

盡管不再有某個(gè)作用域中的任何符號指向這個(gè)結構(就是變量容器),由于數組元素“1”仍然指向數組本身,所以這個(gè)容器不能被清除 。因為沒(méi)有另外的符號指向它,用戶(hù)沒(méi)有辦法清除這個(gè)結構,結果就會(huì )導致內存泄漏。慶幸的是,php將在腳本執行結束時(shí)清除這個(gè)數據結構,但是在php清除之前,將耗費不少內存。如果你要實(shí)現分析算法,或者要做其他像一個(gè)子元素指向它的父元素這樣的事情,這種情況就會(huì )經(jīng)常發(fā)生。當然,同樣的情況也會(huì )發(fā)生在對象上,實(shí)際上對象更有可能出現這種情況,因為對象總是隱式的被引用。

如果上面的情況發(fā)生僅僅一兩次倒沒(méi)什么,但是如果出現幾千次,甚至幾十萬(wàn)次的內存泄漏,這顯然是個(gè)大問(wèn)題。這樣的問(wèn)題往往發(fā)生在長(cháng)時(shí)間運行的腳本中,比如請求基本上不會(huì )結束的守護進(jìn)程(deamons)或者單元測試中的大的套件(sets)中。后者的例子:在給巨大的eZ(一個(gè)知名的PHP Library) 組件庫的模板組件做單元測試時(shí),就可能會(huì )出現問(wèn)題。有時(shí)測試可能需要耗用2GB的內存,而測試服務(wù)器很可能沒(méi)有這么大的內存。

男女疯狂一边摸一边做羞羞视频|啊好深好硬快点用力别停动态图|亚洲一区无码中文字幕|特级无码毛片免费视频播放▽|久久狠狠躁免费观看|国内精品久久久久久网站