濮阳杆衣贸易有限公司

主頁(yè) > 知識(shí)庫(kù) > PHP賦值的內(nèi)部是如何跑的詳解

PHP賦值的內(nèi)部是如何跑的詳解

熱門(mén)標(biāo)簽:兼職做地圖標(biāo)注好賺錢(qián)嗎 400 電話 辦理 亳州企業(yè)外呼系統(tǒng) 蘇州外呼系統(tǒng)有效果嗎 地圖標(biāo)注怎么做商戶(hù)驗(yàn)證 海南外呼系統(tǒng)方案 山東電銷(xiāo)卡外呼系統(tǒng)原理是什么 智能電銷(xiāo)語(yǔ)音機(jī)器人資訊 打開(kāi)百度地圖標(biāo)注

前言

在PHP中,一個(gè)變量被賦值,內(nèi)部到底經(jīng)歷了怎樣的邏輯判斷呢?

PHP在內(nèi)核中是通過(guò)zval這個(gè)結(jié)構(gòu)體來(lái)存儲(chǔ)變量的,它的定義在Zend/zend.h文件里

struct _zval_struct {zvalue_value value; /* 變量的值 */zend_uint refcount__gc;zend_uchar type; /* 變量當(dāng)前的數(shù)據(jù)類(lèi)型 */zend_uchar is_ref__gc;};typedef struct _zval_struct zval;//在Zend/zend_types.h里定義的:typedef unsigned int zend_uint;typedef unsigned char zend_uchar;

使用xdebug的xdebug_debug_zval函數(shù)可以打印出變量的refcount,is_ref的值。

$a = 'Hello World';$b = $a;

以上內(nèi)容在內(nèi)核中怎么執(zhí)行呢?

zval *helloval;MAKE_STD_ZVAL(helloval);ZVAL_STRING(helloval, "Hello World", 1);zend_hash_add(EG(active_symbol_table), "a", sizeof("a"),helloval, sizeof(zval*), NULL);ZVAL_ADDREF(helloval); //這句很特殊,我們顯式的增加了helloval結(jié)構(gòu)體的refcountzend_hash_add(EG(active_symbol_table), "b", sizeof("b"),helloval, sizeof(zval*), NULL);

可以看出來(lái),當(dāng)變量賦值的時(shí)候,其實(shí)兩個(gè)變量指向的是同一個(gè)地址空間。那么問(wèn)題來(lái)了,如果指向同一個(gè)地址空間,那不是修改a,b也會(huì)跟著改變。這就涉及php的 寫(xiě)時(shí)復(fù)制機(jī)制 。 以上代碼,如果后面一行為 $b = '123' 判斷過(guò)程如下:

  • 如果這個(gè)變量的zval部分的refcount小于2,代表沒(méi)有別的變量在用,則直接修改這個(gè)值
  • 否則,復(fù)制一份zval 的值,減少原zval的refcount的值,初始化新的zval的refcount,修改新復(fù)制的zval

簡(jiǎn)單變量

先引用賦值后普通賦值

var_dump(memory_get_usage());$a = '1234567890';xdebug_debug_zval('a');var_dump(memory_get_usage());$b = $a;xdebug_debug_zval('a','b');var_dump(memory_get_usage());$c = $a;xdebug_debug_zval('a','b','c');var_dump(memory_get_usage());$a = '1234567890';var_dump(memory_get_usage());$b = $a;var_dump(memory_get_usage());$c = $a;

輸出內(nèi)容如下:

int(121672)
a: (refcount=1, is_ref=0)='1234567890'

int(121776)
a: (refcount=2, is_ref=1)='1234567890'
b: (refcount=2, is_ref=1)='1234567890'

int(121824)
a: (refcount=2, is_ref=1)='1234567890'
b: (refcount=2, is_ref=1)='1234567890'
c: (refcount=1, is_ref=0)='1234567890'

int(121928)

$a 賦值,開(kāi)辟了104byte空間,變量a refcount=1,is_ref=0

$b 賦值,開(kāi)辟了48byte空間,變量a refcount=2,is_ref=1。48byte是符號(hào)表占用,a,b執(zhí)行同一個(gè)地址空間

$c 賦值,開(kāi)辟了104byte空間。由于a,b是引用,所以在c賦值的時(shí)候,會(huì)開(kāi)辟新空間,復(fù)制a zval內(nèi)容,并初始化refcount,is_ref,所以a 的refcount不變,c 的refcount=1

先普通賦值后引用賦值

var_dump(memory_get_usage());$a = '1234567890';xdebug_debug_zval('a');var_dump(memory_get_usage());$b = $a;xdebug_debug_zval('a','b');var_dump(memory_get_usage());$c = $a;xdebug_debug_zval('a','b','c');var_dump(memory_get_usage());

輸出內(nèi)容如下:

int(121672)

a: (refcount=1, is_ref=0)='1234567890'
int(121776)

a: (refcount=2, is_ref=0)='1234567890'
b: (refcount=2, is_ref=0)='1234567890'
int(121824)

a: (refcount=2, is_ref=1)='1234567890'
b: (refcount=1, is_ref=0)='1234567890'
c: (refcount=2, is_ref=1)='1234567890'
int(121928)

$a 賦值,開(kāi)辟了104byte空間,變量a refcount=1,is_ref=0

$b 賦值,開(kāi)辟了48byte空間,變量a refcount=2,is_ref=1。48byte是符號(hào)表占用,a,b指向同一個(gè)地址空間

$c 賦值,開(kāi)辟了104byte空間。由于a,c是引用,需要與b隔離開(kāi)來(lái),因此會(huì)賦值原有的zval,初始化zval,將a,c指向新復(fù)制的zval,同時(shí)原有的zval refcount-1

數(shù)組

$arr = [0=>'one'];
xdebug_debug_zval('arr');
$arr[1] = $arr;

xdebug_debug_zval('arr');

$arr[2] = $arr;
xdebug_debug_zval('arr');
unset($arr[1]);
xdebug_debug_zval('arr');
unset($arr[2]);
xdebug_debug_zval('arr');

輸出內(nèi)容如下:

arr: (refcount=1, is_ref=0)=array ( 0 => (refcount=1, is_ref=0)='one')
)
arr: (refcount=1, is_ref=0)=array (
0 => (refcount=2, is_ref=0)='one',
1 => (refcount=1, is_ref=0)=array (
0 => (refcount=2, is_ref=0)='one'
)
)
arr: (refcount=1, is_ref=0)=array (
0 => (refcount=3, is_ref=0)='one',
1 => (refcount=2, is_ref=0)=array (
0 => (refcount=3, is_ref=0)='one'),
2 => (refcount=1, is_ref=0)=array (
0 => (refcount=3, is_ref=0)='one',
1 => (refcount=2, is_ref=0)=array (...)
)
)
arr: (refcount=1, is_ref=0)=array (
0 => (refcount=3, is_ref=0)='one',
2 => (refcount=1, is_ref=0)=array (
0 => (refcount=3, is_ref=0)='one',
1 => (refcount=1, is_ref=0)=array (...)
)
)
arr: (refcount=1, is_ref=0)=array (
0 => (refcount=1, is_ref=0)='one'
)
$arr = [0=>'one'];xdebug_debug_zval('arr');$arr[1] = $arr;xdebug_debug_zval('arr');$arr[2] = $arr;xdebug_debug_zval('arr');unset($arr[1]);xdebug_debug_zval('arr');unset($arr[2]);xdebug_debug_zval('arr');

輸出內(nèi)容如下:

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

上面段測(cè)試代碼很相似,差別只在arr[1]是否是引用賦值。

arr[1]非引用賦值的情況,arr[0]的refcount = 賦值次數(shù)+1,執(zhí)行兩次unset之后,arr,arr[0]的refcount都跟開(kāi)始定義的時(shí)候一致。 arr[1]引用賦值的情況,arr[0]的refcount = 非引用賦值次數(shù)+1,執(zhí)行兩次unset之后,arr,arr[0] 的refcount都無(wú)法回到定義的時(shí)候的值。

主要原因在于arr[1]引用賦值,構(gòu)成一個(gè)遞歸操作。 但是如果,至于這個(gè)refcount,真的說(shuō)不明白。當(dāng)沒(méi)有arr[2]賦值的時(shí)候,執(zhí)行unset, arr refcount能回到1 。從下面這張圖更加清晰看出內(nèi)部遞歸引用


當(dāng)出現(xiàn)上面這種情況,refcount本該=1,但實(shí)際上面沒(méi)有被設(shè)置為1,這種情況就會(huì)出現(xiàn)內(nèi)存泄漏。上面代碼循環(huán)執(zhí)行100次,內(nèi)存從一開(kāi)始121096 上升到169224,內(nèi)存占用上升了5k 。

對(duì)象

$user = new User();
 $m = $user;
 $user->user ='';
 $user->name = 'sdfsdfs';
 xdebug_debug_zval('user','m');

以上內(nèi)容輸出

(refcount=2, is_ref=0)=class User {
public $name = (refcount=1, is_ref=0)='sdfsdfs';
public $model = (refcount=1, is_ref=0)=NULL;
public $user = (refcount=1, is_ref=0)=''
}
m: (refcount=2, is_ref=0)=class User {
public $name = (refcount=1, is_ref=0)='sdfsdfs';
public $model = (refcount=1, is_ref=0)=NULL;
public $user = (refcount=1, is_ref=0)=''
}

xdebug給出的is_ref=0。refcount與普通變量一直。但是類(lèi)的賦值是引用賦值。

$user = new User();
 $user->user = $user;
 $user->name = 'sdfsdfs';
 xdebug_debug_zval('user');
 unset($user);

上面內(nèi)容輸出:

user: (refcount=2, is_ref=0)=class User { public $name = (refcount=1, is_ref=0)='sdfsdfs'; public $user = (refcount=2, is_ref=0)=... }

這里由于類(lèi)的賦值是引用賦值,索引也構(gòu)成了一個(gè)遞歸操作,這樣也會(huì)跟數(shù)組一樣出現(xiàn)內(nèi)存泄漏的情況。對(duì)以下代碼個(gè)自行100次

$user = new User();
 $user->user = $user;
 $user->name = 'sdfsdfs';
 xdebug_debug_zval('user');
 unset($user);
$user = new User();
 $user->user = new Order();
 $user->name = 'sdfsdfs';
 xdebug_debug_zval('user');
 unset($user);

第一段代碼前后內(nèi)存差1408 byte. 第二段代碼差208 byte。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

您可能感興趣的文章:
  • php 傳值賦值與引用賦值的區(qū)別
  • PHP讀取txt文件的內(nèi)容并賦值給數(shù)組的代碼
  • 詳解PHP數(shù)組賦值方法
  • php foreach 使用(與運(yùn)算符)引用賦值要注意的問(wèn)題
  • php中給js數(shù)組賦值方法
  • PHP5 的對(duì)象賦值機(jī)制介紹
  • PHP變量賦值、代入給JavaScript中的變量
  • php數(shù)組操作之鍵名比較與差集、交集賦值的方法
  • PHP基礎(chǔ)陷阱題(變量賦值)

標(biāo)簽:綏化 呼倫貝爾 萊蕪 金華 安康 紹興 清遠(yuǎn) 溫州

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《PHP賦值的內(nèi)部是如何跑的詳解》,本文關(guān)鍵詞  PHP,賦值,的,內(nèi)部,是,如何,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《PHP賦值的內(nèi)部是如何跑的詳解》相關(guān)的同類(lèi)信息!
  • 本頁(yè)收集關(guān)于PHP賦值的內(nèi)部是如何跑的詳解的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    潼关县| 承德县| 乐陵市| 隆回县| 宁波市| 遂溪县| 安阳市| 河曲县| 濉溪县| 新密市| 河曲县| 平阳县| 宁明县| 阳信县| 木兰县| 依兰县| 怀来县| 天水市| 房山区| 竹山县| 洛浦县| 全南县| 隆子县| 绍兴县| 民县| 许昌县| 呼伦贝尔市| 乌兰浩特市| 宝应县| 建始县| 黑山县| 隆安县| 中西区| 荣昌县| 怀柔区| 博白县| 枝江市| 囊谦县| 高台县| 海安县| 根河市|