一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - PHP教程 - PHP賦值的內部是如何跑的詳解

PHP賦值的內部是如何跑的詳解

2019-07-03 18:34寫PHP的老王 PHP教程

這篇文章主要給大家介紹了關于PHP賦值的內部是如何跑的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

前言

在PHP中,一個變量被賦值,內部到底經歷了怎樣的邏輯判斷呢?

PHP在內核中是通過zval這個結構體來存儲變量的,它的定義在Zend/zend.h文件里

struct _zval_struct {zvalue_value value; /* 變量的值 */zend_uint refcount__gc;zend_uchar type; /* 變量當前的數據類型 */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函數可以打印出變量的refcount,is_ref的值。

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

以上內容在內核中怎么執行呢?

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結構體的refcountzend_hash_add(EG(active_symbol_table), "b", sizeof("b"),&helloval, sizeof(zval*), NULL);

可以看出來,當變量賦值的時候,其實兩個變量指向的是同一個地址空間。那么問題來了,如果指向同一個地址空間,那不是修改a,b也會跟著改變。這就涉及php的 寫時復制機制 。 以上代碼,如果后面一行為 $b = '123' 判斷過程如下:

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

簡單變量

先引用賦值后普通賦值

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;

輸出內容如下:

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 賦值,開辟了104byte空間,變量a refcount=1,is_ref=0

$b 賦值,開辟了48byte空間,變量a refcount=2,is_ref=1。48byte是符號表占用,a,b執行同一個地址空間

$c 賦值,開辟了104byte空間。由于a,b是引用,所以在c賦值的時候,會開辟新空間,復制a zval內容,并初始化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());

輸出內容如下:

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 賦值,開辟了104byte空間,變量a refcount=1,is_ref=0

$b 賦值,開辟了48byte空間,變量a refcount=2,is_ref=1。48byte是符號表占用,a,b指向同一個地址空間

$c 賦值,開辟了104byte空間。由于a,c是引用,需要與b隔離開來,因此會賦值原有的zval,初始化zval,將a,c指向新復制的zval,同時原有的zval refcount-1

數組

$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');

輸出內容如下:

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');

輸出內容如下:

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'
)

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

arr[1]非引用賦值的情況,arr[0]的refcount = 賦值次數+1,執行兩次unset之后,arr,arr[0]的refcount都跟開始定義的時候一致。 arr[1]引用賦值的情況,arr[0]的refcount = 非引用賦值次數+1,執行兩次unset之后,arr,arr[0] 的refcount都無法回到定義的時候的值。

主要原因在于arr[1]引用賦值,構成一個遞歸操作。 但是如果,至于這個refcount,真的說不明白。當沒有arr[2]賦值的時候,執行unset, arr refcount能回到1 。從下面這張圖更加清晰看出內部遞歸引用

PHP賦值的內部是如何跑的詳解

當出現上面這種情況,refcount本該=1,但實際上面沒有被設置為1,這種情況就會出現內存泄漏。上面代碼循環執行100次,內存從一開始121096 上升到169224,內存占用上升了5k 。

對象

$user = new User();
 $m = $user;
 $user->user ='';
 $user->name = 'sdfsdfs';
 xdebug_debug_zval('user','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)='' 
}
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與普通變量一直。但是類的賦值是引用賦值。

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

上面內容輸出:

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

這里由于類的賦值是引用賦值,索引也構成了一個遞歸操作,這樣也會跟數組一樣出現內存泄漏的情況。對以下代碼個自行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);

第一段代碼前后內存差1408 byte. 第二段代碼差208 byte。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 91系列在线观看免费 | julia ann一hd| 性夜影院爽黄A爽免费动漫 性色欲情网站IWWW九文堂 | 日本乱中文字幕系列在线观看 | 福利片福利一区二区三区 | 亚洲精品午夜久久aaa级久久久 | 九九精品国产亚洲A片无码 九九99热久久999精品 | 国产欧美日韩图片一区二区 | 亚洲欧美一区二区三区在饯 | 亚洲国产日韩欧美一区二区三区 | 我被男人下药添得好爽 | 99国产精品免费视频 | 国产乱码免费卡1卡二卡3卡四 | 亚洲高清视频免费 | 精品精品国产自在香蕉网 | 色综合色狠狠天天久久婷婷基地 | 白发在线视频播放观看免费 | 亚洲精品久久久WWW游戏好玩 | 日韩a无吗一区二区三区 | 按摩椅play啊太快了h | 陈峰姚瑶全集小说无删节 | 男人看片网址 | 亚洲国产精品线在线观看 | 久久午夜夜伦痒痒想咳嗽P 久久无码AV亚洲精品色午夜麻豆 | 九九99在线视频 | 精品一区二区三区高清免费观看 | 91果冻制片厂天美传媒 | 成年人免费观看 | 国产亚洲综合久久 | 国产精品嫩草影院一二三区入口 | 男人的天堂欧美 | 麻豆网| 国产va免费精品高清在线观看 | 俺去也亚洲色图 | 国精视频一区二区视频 | 午夜毛片在线观看 | 国产亚洲福利一区二区免费看 | 国产盗摄女厕美女嘘嘘 | 9久热久爱免费精品视频在线观看 | 99ri国产在线 | 国产一区国产二区国产三区 |