摘要:引用計(jì)數(shù)變量分離寫時(shí)拷貝我們一步步來(lái)理解語(yǔ)言特性是腳本語(yǔ)言,所謂腳本語(yǔ)言,就是說(shuō)并不是獨(dú)立運(yùn)行的,要運(yùn)行代碼需要解析器,用戶編寫的代碼最終都會(huì)被解析器解析執(zhí)行的執(zhí)行是通過(guò)引擎,是用編寫的用戶編寫的代碼最終都會(huì)被翻譯成的虛擬機(jī)的虛擬指令來(lái)執(zhí)行
zval、引用計(jì)數(shù)、變量分離、寫時(shí)拷貝
我們一步步來(lái)理解
1、php語(yǔ)言特性
PHP是腳本語(yǔ)言,所謂腳本語(yǔ)言,就是說(shuō)PHP并不是獨(dú)立運(yùn)行的,要運(yùn)行PHP代碼需要PHP解析器,用戶編寫的PHP代碼最終都會(huì)被PHP解析器解析執(zhí)行
PHP的執(zhí)行是通過(guò)Zend engine(ZE, Zend引擎),ZE是用C編寫的
用戶編寫的PHP代碼最終都會(huì)被翻譯成PHP的虛擬機(jī)ZE的虛擬指令(OPCODES)來(lái)執(zhí)行
也就說(shuō)最終會(huì)被翻譯成一條條的指令
既然這樣,有什么結(jié)果和你預(yù)想的不一樣,查看php源碼是最直接最有效的
2、php變量的存儲(chǔ)結(jié)構(gòu)
在PHP中,所有的變量都是用一個(gè)結(jié)構(gòu)zval結(jié)構(gòu)來(lái)保存的,在Zend/zend.h中可以看到zval的定義:
zval結(jié)構(gòu)包括:
① value —— 值,是真正保存數(shù)據(jù)的關(guān)鍵部分,定義為一個(gè)聯(lián)合體(union)
② type —— 用來(lái)儲(chǔ)存變量的類型
③ is_ref —— 下面介紹
④ refcount —— 下面介紹
聲明一個(gè)變量
$addr="北京";
PHP內(nèi)部都是使用zval來(lái)表示變量的,那對(duì)于上面的腳本,ZE是如何把a(bǔ)ddr和內(nèi)部的zval結(jié)構(gòu)聯(lián)系起來(lái)的呢?
變量都是有名字的(本例中變量名為addr)
而zval中并沒(méi)有相應(yīng)的字段來(lái)體現(xiàn)變量名。PHP內(nèi)部肯定有一個(gè)機(jī)制,來(lái)實(shí)現(xiàn)變量名到zval的映射
在PHP中,所有的變量都會(huì)存儲(chǔ)在一個(gè)數(shù)組中(確切的說(shuō)是hash table)
當(dāng)你創(chuàng)建一個(gè)變量的時(shí)候,PHP會(huì)為這個(gè)變量分配一個(gè)zval,填入相應(yīng)的信息,然后將這個(gè)變量的名字和指向這個(gè)zval的指針填入一個(gè)數(shù)組中。當(dāng)你獲取這個(gè)變量的時(shí)候,PHP會(huì)通過(guò)查找這個(gè)數(shù)組,取得對(duì)應(yīng)的zval
注意:數(shù)組和對(duì)象這類復(fù)合類型在生成zval時(shí),會(huì)為每個(gè)單元生成一個(gè)zval
3、我們經(jīng)常說(shuō)每個(gè)變量都有一個(gè)內(nèi)存地址,那這個(gè)zval和變量的內(nèi)存地址,這倆有什么關(guān)系嗎?
定義一個(gè)變量會(huì)開辟一塊內(nèi)存,這塊內(nèi)存好比一個(gè)盒子,盒子里放了zval,zval里保存了變量的相關(guān)信息,需要開辟多大的內(nèi)存,是由zval所占空間大小決定的
zval是內(nèi)存對(duì)象,垃圾回收的時(shí)候會(huì)把zval和內(nèi)存地址(盒子)分別釋放掉
4、引用計(jì)數(shù)、變量分離、寫時(shí)拷貝
zval中的refcount和is_ref還沒(méi)有介紹,我們知道PHP是一個(gè)長(zhǎng)時(shí)間運(yùn)行的服務(wù)器端腳本。那么對(duì)于它來(lái)說(shuō),效率和資源占用率是一個(gè)很重要的衡量標(biāo)準(zhǔn),也就是說(shuō),PHP必須盡量減少內(nèi)存占用率??紤]下面這段代碼:
第一行代碼創(chuàng)建了一個(gè)字符串變量,申請(qǐng)了一個(gè)大小為9字節(jié)的內(nèi)存,保存了字符串“l(fā)aruence”和一個(gè)NULL(