眾所周知,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類的時候通常顯示的定義拷貝建構函式和運算子過載函式。由於釋放記憶體空間,開闢記憶體空間時花費時間,因此,在我們不需要寫,只是讀的時候就可以不用新開闢記憶體空間,就用淺拷貝的方式建立物件,當我...