php變數的引用計數器和寫時複製

2021-07-04 06:04:07 字數 2383 閱讀 3873

眾所周知,php是不支援指標的,但是如果希望兩個變數同時指向同一記憶體塊怎麼辦呢?為了解決這個問題,php核心裡使用了引用計數器。

上篇博文介紹了php變數在核心中的儲存方式了,zval結構中下面兩個成員變數用於引用計數器:

is_ref  bool值,標識變數是否是引用集合。

refcount 計算指向引用集合的變數個數。

看下面的php**

<?php

$a = "this is a";

?>

乙個zval結構的實體稱為zval容器。在php語言層建立乙個變數就會相應地在php核心中建立乙個zval容器。因為上面的**建立了乙個變數$a,所以在php核心中會建立乙個zval容器;又因為這個變數不是乙個引用,所以zval容器的is_ref等於false,並且refcount等於1.

再看下面的**

<?php

$a = "this is a";

$b=$a;

?>

上面這段**建立了兩個變數a和

b,所以php核心會建立兩個zval容器來儲存他們。變數b被

賦予變數

a的值,那麼現在變數a對

應的is

ref字

段為何值

呢?由於

變數並不是引用變數a,

所以變數

a的is_ref欄位的值為false,這個容易理解。但是如果使用xdebug列印變數a的

話,會發

現ref

coun

t等於2

,為什麼

是2呢。

這裡首先

得知道p

hp的寫

時複製機

制。寫時

複製是一

個解決內

存復用的

辦法。例

如上面的

**,如

果簡單地

把 a的值賦給$b,就有兩個」this is a」字串的複製,這樣不利於記憶體復用。因為完全可以使用乙個」this is a」字串的複製完成工作。所以簡單的賦值複製是非常耗費記憶體的,寫時複製就是為了解決這個問題。

寫時複製,就是當變數的值改變時才進行記憶體的複製。要理解寫時複製,先看下面的**:

<?php

$a="this is a";

xdebug_debug_zval('a');

$b=$a;

xdebug_debug_zval('a');

$a="changed value";

xdebug_debug_zval('a');

?>

上面這段**使用xdebug除錯工具。輸出的結果如下:

a:

string 'this is a'

a: string 'this is a'

a: string 'changed value'

上面所示,當將變數a的

值賦給變

量 b時,變數a的

refc

ount

增加1,

所以這時

候變數 a跟變數b是

指向同一

記憶體塊的

。當變數

a的值改變時,發現refcount的值變回1,所以這時變數a和

變數b指向不同的記憶體塊,這就是讀寫複製機制。就是兩個指向同一記憶體塊的變數,當其中乙個變數的值發生變化,才會另外建立乙個記憶體塊去儲存新的值。

看最後一種情況,如果使用者在php指令碼中顯式地讓乙個變數引用另乙個變數,php核心會如何處理呢?看下面的**:

<?php

$a=1;

xdebug_debug_zval('a');

$b=&$a;

xdebug_debug_zval('a');

$b+=5;

xdebug_debug_zval('a');

?>

上面的**輸出結果為:

a:

int 1

a: int 1

a: int 6

可以看到,當顯式地讓乙個變數引用另乙個變數時,變數的is_ref欄位會設定為1,表示此變數被引用,另外引用計數器(refcount)也相應的加1,。而在php核心中通過下面**判斷是否複製變數:

if((*valval)->is_ref ||(*valval)->refcount<2 )
從上面的**中可以知道,當變數被引用或者計數器小於2時會直接返回變數的指標(直接返回變數的實體,而不複製變數的值)。當修改乙個被引用變數的值時,所有引用它的變數其值也會被修改,因為它們指向同乙個zval容器。

引用計數的寫時拷貝

首先我們需要知道什麼是寫時拷貝,通俗的說,就是寫的時候再拷貝。那到底什麼才是寫時拷貝呢?舉乙個很簡單的例子,就是建立乙個string類的物件,然後用這個物件再拷貝出多個物件,當然指標也會拷貝過去,造成多個物件指向同一塊空間,當對某個物件進行讀操作時,不會發生什麼問題,但當需要對某個物件進行寫操作時,...

c 的引用計數與寫時複製

最近看了c 沉思錄,了解一下控制代碼類,其中的引用計數,寫時複製技術很值得學習,特將它摘抄下來,希望它對大家也有用。原始類 class cpoint cpoint int x,int y xval x yval y int x const int y const cpoint x int xv cp...

string類的寫時拷貝與引用計數

由於淺拷貝使多個物件共用一塊記憶體位址,呼叫析構函式時導致一塊記憶體被多次釋放,導致程式奔潰。實現string類的時候通常顯示的定義拷貝建構函式和運算子過載函式。由於釋放記憶體空間,開闢記憶體空間時花費時間,因此,在我們不需要寫,只是讀的時候就可以不用新開闢記憶體空間,就用淺拷貝的方式建立物件,當我...