摘要:如果,且僅僅是如果,引用計(jì)數(shù)變成了,引擎會(huì)銷毀該。的結(jié)構(gòu)重新定義了,都有一個(gè)同樣的頭用來(lái)存儲(chǔ)引用計(jì)數(shù)拷貝引用有兩種方法引用。需要使用指針的復(fù)雜類型比如字符串?dāng)?shù)組和對(duì)象會(huì)自己存儲(chǔ)引用計(jì)數(shù)。
引用計(jì)數(shù)
迄今為止,我們向HashTables中加入的zval要么是新建的,要么是剛拷貝的。它們都是獨(dú)立的,只占用自己的資源且只存在于某個(gè)HashTable中。作為一個(gè)語(yǔ)言設(shè)計(jì)的概念,創(chuàng)建和拷貝變量的方法是“很好”的,但是習(xí)慣了C程序設(shè)計(jì)就會(huì)知道,通過(guò)避免拷貝大塊的數(shù)據(jù)(除非絕對(duì)必須)來(lái)節(jié)約內(nèi)存和CPU時(shí)間并不少見(jiàn)。考慮這段用戶代碼:
如果執(zhí)行zval_copy_ctor()(將會(huì)對(duì)字符串內(nèi)容執(zhí)行estrndup())將$a拷貝給$b,那么這個(gè)簡(jiǎn)短的腳本實(shí)際會(huì)用掉8M內(nèi)存來(lái)存儲(chǔ)同一4M文件的兩份相同的副本。在最后一步取消$a只會(huì)更糟,因?yàn)樵甲址籩free()了。用C做這個(gè)將會(huì)很簡(jiǎn)單,大概是這樣:b = a; a = NULL;。
Zend引擎的做法更聰明。當(dāng)創(chuàng)建$a時(shí),會(huì)創(chuàng)建一個(gè)潛在的string類型的zval,它含有日至文件的內(nèi)容。這個(gè)zval通過(guò)調(diào)用zend_hash_add()被
賦給$a變量。當(dāng)$a被拷貝給$b,引擎做類似下面的事情:{ zval **value; zend_hash_find(EG(active_symbol_table), "a", sizeof("a"), (void**)&value); ZVAL_ADDREF(*value); zend_hash_add(EG(active_symbol_table), "b", sizeof("b"), value,sizeof(zval*)); }實(shí)際代碼會(huì)更復(fù)雜點(diǎn),簡(jiǎn)單的說(shuō),就是通過(guò)引用計(jì)數(shù)來(lái)記錄zval在符號(hào)表中、數(shù)組中、或其他地方被引用的次數(shù)。這樣$b = $a賦值只要將其引用計(jì)數(shù)+1,而不用去進(jìn)行內(nèi)容拷貝。
當(dāng)用戶空間代碼調(diào)用unset($a),引擎對(duì)該變量執(zhí)行zval_ptr_dtor()。在前面用到的zval_ptr_dtor()中,你看不到的事實(shí)是,這個(gè)調(diào)用沒(méi)有必要銷毀該zval和它的內(nèi)容。實(shí)際工作是減少refcount。如果,且僅僅是如果,引用計(jì)數(shù)變成了0,Zend引擎會(huì)銷毀該zval。
有些簡(jiǎn)單數(shù)據(jù)類型不需要多帶帶分配內(nèi)存,也不需要計(jì)數(shù);PHP7中zval的long和double類型是 不需要 引用計(jì)數(shù)的。
php7的zval結(jié)構(gòu)重新定義了,都有一個(gè)同樣的頭(zend_refcounted)用來(lái)存儲(chǔ)引用計(jì)數(shù):
typedef struct _zend_refcounted_h { uint32_t refcount; /* reference counter 32-bit */ union { struct { ZEND_ENDIAN_LOHI_3( zend_uchar type, zend_uchar flags, /* used for strings & objects */ uint16_t gc_info) /* keeps GC root number (or 0) and color */ } v; uint32_t type_info; } u; } zend_refcounted_h;拷貝 vs 引用有兩種方法引用zval。第一種,如上文示范的,被稱為寫復(fù)制引用(copy-on-write referencing)。第二種形式是完全引用(full referencing);當(dāng)說(shuō)起“引用”時(shí),用戶空間代碼的編寫者更熟悉這種, 以用戶空間代碼的形式出現(xiàn)類似于:$a = &$b;。
在zval中,這兩種類型的區(qū)別在于它的is_ref成員的值,0表示寫復(fù)制引用,非0表示完全引用。注意,一個(gè)zval不可能同時(shí)具有兩種引用類型。所以,如果變量起初是is_ref(即完全引用-譯注),然后以拷貝的方式賦給新的變量,那么必將執(zhí)行一個(gè)完全拷貝??紤]下面的用戶空間代碼:
zend_array_1(refcount=1, value=[]) $b = &$a; //$a, $b -> zend_reference_1(refcount=2) -> zend_array_1(refcount=1, value=[]) $c = $a; //// $a, $b, $c -> zend_array_1(refcount=3, value=[])在這段代碼中,為$a創(chuàng)建并初始化了一個(gè)zval,將is_ref設(shè)為0,將refcount設(shè)為1。當(dāng)$a被$b引用時(shí),is_ref變?yōu)?,refcount遞增至2。當(dāng)拷貝至$c時(shí),Zend引擎不能只是遞增refcount至3,因?yàn)槿绱藙t$c變成了$a的完全引用。關(guān)閉is_ref也不行,因?yàn)槿绱藭?huì)使$b看起來(lái)像是$a的一份拷貝而不是引用。所以此時(shí)分配了一個(gè)新的zval,并使用zval_copy_ctor()把原始(zval)的值拷貝給它。原始zval仍為is_ref==1、refcount==2,同時(shí)新zval則為is_ref=0、refcount=1?,F(xiàn)在來(lái)看另一塊內(nèi)容相同的代碼塊,只是順序稍有不同:
zend_array_1(refcount=1, value=[]) $c = $a; // $a, $c -> zval_1(type=IS_ARRAY, refcount=2, is_ref=0) -> HashTable_1(value=[]) $b = &$a; // $c -> zval_1(type=IS_ARRAY, refcount=2, is_ref=0) -> HashTable_1(value=[]) // $b, $a -> zval_1(type=IS_ARRAY, refcount=2, is_ref=1) -> HashTable_2(value=[]) // $b 是 $a 的引用, 但卻不是 $a 的 $c, 所以這里 zval 還是需要進(jìn)行復(fù)制 // 這樣我們就有了兩個(gè) zval, 一個(gè) is_ref 的值是 0, 一個(gè) is_ref 的值是 1.所有的變量都可以共享同一個(gè)數(shù)組,最終結(jié)果不變,$b是$a的完全引用,并且$c是$a的一份拷貝。然而這次的內(nèi)部效果稍有區(qū)別。如前,開(kāi)始時(shí)為$a創(chuàng)建一個(gè)is_ref==0并且refcount=1的新zval。$c = $a;語(yǔ)句將同一個(gè)zval賦給$c變量,同時(shí)將refcount增至2,is_ref仍是0。當(dāng)Zend引擎遇到$b = &$a;,它想要只是將is_ref設(shè)為1,但是當(dāng)然不行,因?yàn)槟菍⒂绊懙?c。所以改為創(chuàng)建新的zval并用zval_copy_ctor()將原始(zval)的內(nèi)容拷貝給它。然后遞減原始zval的refcount以表明$a不再使用該zval。代替地,(Zend)設(shè)置新zval的is_ref為1、refcount為2,并且更新$a和$b變量指向它(新zval)。
zend_array_1(refcount=1, value=[]) $b = $a; // $a = zval_1(type=IS_ARRAY) -> zend_array_1(refcount=2, value=[]) // $b = zval_2(type=IS_ARRAY) ---^ // zval 分離在這里進(jìn)行 $a[] = 1 // $a = zval_1(type=IS_ARRAY) -> zend_array_2(refcount=1, value=[1]) // $b = zval_2(type=IS_ARRAY) -> zend_array_1(refcount=1, value=[]) unset($a); // $a = zval_1(type=IS_UNDEF), zend_array_2 被銷毀 // $b = zval_2(type=IS_ARRAY) -> zend_array_1(refcount=1, value=[])這個(gè)過(guò)程其實(shí)挺簡(jiǎn)單的?,F(xiàn)在整數(shù)不再是共享的,變量直接就會(huì)分離成兩個(gè)多帶帶的 zval,由于現(xiàn)在 zval 是內(nèi)嵌的所以也不需要多帶帶分配內(nèi)存,所以這里的注釋中使用 = 來(lái)表示的而不是指針?lè)?hào) ->,unset 時(shí)變量會(huì)被標(biāo)記為 IS_UNDEF。
總結(jié)PHP7 中最重要的改變就是 zval 不再多帶帶從堆上分配內(nèi)存并且不自己存儲(chǔ)引用計(jì)數(shù)。需要使用 zval 指針的復(fù)雜類型(比如字符串、數(shù)組和對(duì)象)會(huì)自己存儲(chǔ)引用計(jì)數(shù)。這樣就可以有更少的內(nèi)存分配操作、更少的間接指針使用以及更少的內(nèi)存分配。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/22093.html
摘要:本文主要是針對(duì),的話可以移步到慶哥的博客看,還有就是小菜我讀的是內(nèi)核剖析這本書(shū)。接下來(lái)我會(huì)使用到來(lái)調(diào)試源碼本文有參照博客中的部分內(nèi)容以及代碼。 前言 工作+實(shí)習(xí)快一年了,搞php后端開(kāi)發(fā),一直很迷茫怎么提高自己,就先從php源碼開(kāi)始吧,本人比較菜,本文章寫的比較趕時(shí)間,所以有什么錯(cuò)誤或者漏掉的地方,望各位大神指正,多交流才能成長(zhǎng)嘛,嘿嘿。本文主要是針對(duì)php7,php5的話可以移步到慶...
摘要:但在密集計(jì)算方面比等靜態(tài)編譯語(yǔ)言差幾十倍甚至上百倍。一使用棧內(nèi)存在引擎和擴(kuò)展中,經(jīng)常要?jiǎng)?chuàng)建一個(gè)的變量,底層就是一個(gè)指針。代碼中創(chuàng)建的變量也進(jìn)行了優(yōu)化,直接在棧內(nèi)存上預(yù)分配。應(yīng)用層與底層在錯(cuò)誤拋出的方式全部統(tǒng)一為異常。 原文:http://rango.swoole.com/archives/440最近PHP官方終于發(fā)布了傳說(shuō)中的PHP7,雖然只是alpha版。PHP7號(hào)稱是新一代的PHP...
摘要:中基礎(chǔ)中的三大坑,遍歷,引用機(jī)制,數(shù)組。今天我們?cè)谥v講中的一些奇怪現(xiàn)象。本文適合有一定基礎(chǔ)的。運(yùn)行流程共用一個(gè)結(jié)構(gòu)體開(kāi)始遍歷數(shù)組,進(jìn)行判斷,拷貝數(shù)組是一個(gè)新的結(jié)構(gòu)體,操作的是新的結(jié)構(gòu)體。那么遍歷數(shù)組時(shí),全程與原數(shù)組無(wú)關(guān)。 PHP中基礎(chǔ)中的三大坑,foreach遍歷,引用機(jī)制&,數(shù)組。 今天我們?cè)谥v講foreach中的一些奇怪現(xiàn)象。 在講解之前,可以先看看我其他相關(guān)的文章,屬于同一個(gè)大的...
摘要:編譯工具這個(gè)腳本主要生成了編譯需要的配置以及擴(kuò)展的基本結(jié)構(gòu)這個(gè)腳本主要是獲取的安裝信息用于生成文件編寫擴(kuò)展的基本步驟通過(guò)目錄下腳本生成擴(kuò)展的基本框架修改配置設(shè)置編譯配置參數(shù)設(shè)置擴(kuò)展的源文件依賴庫(kù)函數(shù)檢查等等定義一個(gè)這樣的編譯參數(shù) 1. 編譯工具 (a).ext_skel:這個(gè)腳本主要生成了編譯需要的配置以及擴(kuò)展的基本結(jié)構(gòu) (b).php-config:這個(gè)腳本主要是獲取PHP的安裝信息...
摘要:起步很多時(shí)候,需要把控制權(quán)限交給用戶,或者在擴(kuò)展里完成某件事后去回調(diào)用戶的方法。在擴(kuò)展里是通過(guò)函數(shù)來(lái)調(diào)用用戶空間的函數(shù)的。收集回調(diào)函數(shù)的返回值。回調(diào)函數(shù)需要傳遞參數(shù)的個(gè)數(shù)。利用語(yǔ)言多線程庫(kù)來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的并行擴(kuò)展。 起步 很多時(shí)候,需要把控制權(quán)限交給用戶,或者在擴(kuò)展里完成某件事后去回調(diào)用戶的方法。 在PHP擴(kuò)展里是通過(guò) call_user_function_ex 函數(shù)來(lái)調(diào)用用戶空間的函...
閱讀 3996·2021-09-27 13:36
閱讀 4757·2021-09-22 15:12
閱讀 3182·2021-09-13 10:29
閱讀 1922·2021-09-10 10:50
閱讀 2482·2021-09-03 10:43
閱讀 616·2019-08-29 17:10
閱讀 508·2019-08-26 13:52
閱讀 3365·2019-08-23 14:37