最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時間:2019年01月13日 14:38:11   作者:寫PHP的老王  
這篇文章主要給大家介紹了關(guān)于PHP賦值的內(nèi)部是如何跑的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

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

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

struct _zval_struct {zvalue_value value; /* 變量的值 */zend_uint refcount__gc;zend_uchar type; /* 變量當(dāng)前的數(shù)據(jù)類型 */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);

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

  • 如果這個變量的zval部分的refcount小于2,代表沒有別的變量在用,則直接修改這個值
  • 否則,復(fù)制一份zval 的值,減少原zval的refcount的值,初始化新的zval的refcount,修改新復(fù)制的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;

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

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

$c 賦值,開辟了104byte空間。由于a,b是引用,所以在c賦值的時候,會開辟新空間,復(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 賦值,開辟了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指向新復(fù)制的zval,同時原有的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'
)

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

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

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

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

對象

$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與普通變量一直。但是類的賦值是引用賦值。

$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)=... }

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

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

相關(guān)文章

  • thinkphp查詢,3.X 5.0方法(親試可行)

    thinkphp查詢,3.X 5.0方法(親試可行)

    下面小編就為大家?guī)硪黄猼hinkphp查詢,3.X 5.0方法(親試可行)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • php提示Failed to write session data錯誤的解決方法

    php提示Failed to write session data錯誤的解決方法

    這篇文章主要介紹了php提示Failed to write session data錯誤的解決方法,較為詳細(xì)的分析了session寫入錯誤的原因與解決方法,并附帶說明了php的工作機(jī)制,非常具有實(shí)用價值,需要的朋友可以參考下
    2014-12-12
  • PHP Token(令牌)設(shè)計(jì)

    PHP Token(令牌)設(shè)計(jì)

    PHP Token(令牌)設(shè)計(jì) 設(shè)計(jì)目標(biāo): 避免重復(fù)提交數(shù)據(jù). 檢查來路,是否是外部提交 匹配要執(zhí)行的動作(如果有多個邏輯在同一個頁面實(shí)現(xiàn),比如新增,刪除,修改放到一個PHP文件里操作) 這里所說的token是在頁面顯示的時候,寫到FORM的一個隱藏表單項(xiàng)(type=hidden). token不可明文,如果是明文,那就太危險了,所以要采用一定的加密方式.密文要可逆.俺算法很白癡,所以采用了網(wǎng)上一個現(xiàn)成的方法.
    2008-03-03
  • PHP基于數(shù)組實(shí)現(xiàn)的堆棧和隊(duì)列功能示例

    PHP基于數(shù)組實(shí)現(xiàn)的堆棧和隊(duì)列功能示例

    這篇文章主要介紹了PHP基于數(shù)組實(shí)現(xiàn)的堆棧和隊(duì)列功能,結(jié)合實(shí)例形式分析了php基于數(shù)組的array_push()、array_pop()、array_shift()等函數(shù)實(shí)現(xiàn)堆棧與隊(duì)列的入棧、出棧以及隊(duì)列的添加、刪除等相關(guān)操作技巧,需要的朋友可以參考下
    2018-01-01
  • php 使用html5 XHR2實(shí)現(xiàn)上傳文件與進(jìn)度顯示功能示例

    php 使用html5 XHR2實(shí)現(xiàn)上傳文件與進(jìn)度顯示功能示例

    這篇文章主要介紹了php 使用html5 XHR2實(shí)現(xiàn)上傳文件與進(jìn)度顯示功能,結(jié)合實(shí)例形式分析了php 使用html5上傳文件過程中progress事件返回進(jìn)度信息相關(guān)操作技巧,需要的朋友可以參考下
    2020-03-03
  • Php Mssql操作簡單封裝支持存儲過程

    Php Mssql操作簡單封裝支持存儲過程

    沒有啥太多的功能,就是簡單封裝,也沒有緩存,平時用ezSQL,但是ezSQL不支持存儲過程,所以這里封裝了存儲過程,因?yàn)樽约旱男枰?,只是做一個標(biāo)記在這里而已。
    2009-12-12
  • 詳解php處理大并發(fā)大流量大存儲

    詳解php處理大并發(fā)大流量大存儲

    這篇文章主要介紹了詳解php處理大并發(fā)、大流量、大存儲,對此感興趣的同學(xué),一定要看一下
    2021-04-04
  • 利用PHP計(jì)算有多少小于當(dāng)前數(shù)字的數(shù)字方法示例

    利用PHP計(jì)算有多少小于當(dāng)前數(shù)字的數(shù)字方法示例

    這篇文章主要給大家介紹了關(guān)于利用PHP計(jì)算有多少小于當(dāng)前數(shù)字的數(shù)字的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • PHP新特性詳解之命名空間、性狀與生成器

    PHP新特性詳解之命名空間、性狀與生成器

    這篇文章主要給大家介紹了關(guān)于PHP新特性之命名空間、性狀與生成器的相關(guān)資料,主要涉及到php中trait(性狀)與generator(生成器) 方面的內(nèi)容,對于php中trait(性狀)與generator(生成器) 感興趣的同學(xué)可以參考一下。
    2017-07-07
  • PHP實(shí)現(xiàn)通過URL提取根域名

    PHP實(shí)現(xiàn)通過URL提取根域名

    本文給大家分享的是個人在做項(xiàng)目的時候遇到的,需要通過php實(shí)現(xiàn)從URL中提取根域名的代碼以及實(shí)現(xiàn)思路,有需要的小伙伴可以參考下
    2016-03-03

最新評論

开封市| 东丰县| 抚远县| 普宁市| 北碚区| 长葛市| 卢龙县| 绵竹市| 凉城县| 怀柔区| 四平市| 突泉县| 宣化县| 新干县| 西华县| 红河县| 周宁县| 清水河县| 达拉特旗| 安岳县| 尼勒克县| 临汾市| 社会| 句容市| 辉县市| 邻水| 平江县| 息烽县| 清丰县| 呼和浩特市| 徐汇区| 镇赉县| 葫芦岛市| 巴彦县| 晋江市| 定南县| 马鞍山市| 漳州市| 揭阳市| 东乡| 安多县|