国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

PHP7擴展開發(四):拷貝與引用

PingCAP / 955人閱讀

摘要:如果,且僅僅是如果,引用計數變成了,引擎會銷毀該。的結構重新定義了,都有一個同樣的頭用來存儲引用計數拷貝引用有兩種方法引用。需要使用指針的復雜類型比如字符串數組和對象會自己存儲引用計數。

引用計數

迄今為止,我們向HashTables中加入的zval要么是新建的,要么是剛拷貝的。它們都是獨立的,只占用自己的資源且只存在于某個HashTable中。作為一個語言設計的概念,創建和拷貝變量的方法是“很好”的,但是習慣了C程序設計就會知道,通過避免拷貝大塊的數據(除非絕對必須)來節約內存和CPU時間并不少見。考慮這段用戶代碼:


如果執行zval_copy_ctor()(將會對字符串內容執行estrndup())將$a拷貝給$b,那么這個簡短的腳本實際會用掉8M內存來存儲同一4M文件的兩份相同的副本。在最后一步取消$a只會更糟,因為原始字符串被efree()了。用C做這個將會很簡單,大概是這樣:b = a; a = NULL;。

Zend引擎的做法更聰明。當創建$a時,會創建一個潛在的string類型的zval,它含有日至文件的內容。這個zval通過調用zend_hash_add()被
賦給$a變量。當$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*));
}

實際代碼會更復雜點,簡單的說,就是通過引用計數來記錄zval在符號表中、數組中、或其他地方被引用的次數。這樣$b = $a賦值只要將其引用計數+1,而不用去進行內容拷貝。

當用戶空間代碼調用unset($a),引擎對該變量執行zval_ptr_dtor()。在前面用到的zval_ptr_dtor()中,你看不到的事實是,這個調用沒有必要銷毀該zval和它的內容。實際工作是減少refcount。如果,且僅僅是如果,引用計數變成了0,Zend引擎會銷毀該zval。

有些簡單數據類型不需要多帶帶分配內存,也不需要計數;PHP7中zval的long和double類型是 不需要 引用計數的。

php7的zval結構重新定義了,都有一個同樣的頭(zend_refcounted)用來存儲引用計數:

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。第一種,如上文示范的,被稱為寫復制引用(copy-on-write referencing)。第二種形式是完全引用(full referencing);當說起“引用”時,用戶空間代碼的編寫者更熟悉這種, 以用戶空間代碼的形式出現類似于:$a = &$b;

在zval中,這兩種類型的區別在于它的is_ref成員的值,0表示寫復制引用,非0表示完全引用。注意,一個zval不可能同時具有兩種引用類型。所以,如果變量起初是is_ref(即完全引用-譯注),然后以拷貝的方式賦給新的變量,那么必將執行一個完全拷貝。考慮下面的用戶空間代碼:

 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創建并初始化了一個zval,將is_ref設為0,將refcount設為1。當$a被$b引用時,is_ref變為1,refcount遞增至2。當拷貝至$c時,Zend引擎不能只是遞增refcount至3,因為如此則$c變成了$a的完全引用。關閉is_ref也不行,因為如此會使$b看起來像是$a的一份拷貝而不是引用。所以此時分配了一個新的zval,并使用zval_copy_ctor()把原始(zval)的值拷貝給它。原始zval仍為is_ref==1、refcount==2,同時新zval則為is_ref=0、refcount=1。現在來看另一塊內容相同的代碼塊,只是順序稍有不同:

 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 還是需要進行復制
          // 這樣我們就有了兩個 zval, 一個 is_ref 的值是 0, 一個 is_ref 的值是 1.

所有的變量都可以共享同一個數組,最終結果不變,$b是$a的完全引用,并且$c是$a的一份拷貝。然而這次的內部效果稍有區別。如前,開始時為$a創建一個is_ref==0并且refcount=1的新zval。$c = $a;語句將同一個zval賦給$c變量,同時將refcount增至2,is_ref仍是0。當Zend引擎遇到$b = &$a;,它想要只是將is_ref設為1,但是當然不行,因為那將影響到$c。所以改為創建新的zval并用zval_copy_ctor()將原始(zval)的內容拷貝給它。然后遞減原始zval的refcount以表明$a不再使用該zval。代替地,(Zend)設置新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 分離在這里進行
$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=[])

這個過程其實挺簡單的。現在整數不再是共享的,變量直接就會分離成兩個多帶帶的 zval,由于現在 zval 是內嵌的所以也不需要多帶帶分配內存,所以這里的注釋中使用 = 來表示的而不是指針符號 ->,unset 時變量會被標記為 IS_UNDEF。

總結

PHP7 中最重要的改變就是 zval 不再多帶帶從堆上分配內存并且不自己存儲引用計數。需要使用 zval 指針的復雜類型(比如字符串、數組和對象)會自己存儲引用計數。這樣就可以有更少的內存分配操作、更少的間接指針使用以及更少的內存分配。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/22093.html

相關文章

  • php7內核閱讀(1)--數據容器zval和zend_value

    摘要:本文主要是針對,的話可以移步到慶哥的博客看,還有就是小菜我讀的是內核剖析這本書。接下來我會使用到來調試源碼本文有參照博客中的部分內容以及代碼。 前言 工作+實習快一年了,搞php后端開發,一直很迷茫怎么提高自己,就先從php源碼開始吧,本人比較菜,本文章寫的比較趕時間,所以有什么錯誤或者漏掉的地方,望各位大神指正,多交流才能成長嘛,嘿嘿。本文主要是針對php7,php5的話可以移步到慶...

    canger 評論0 收藏0
  • 【天贏金創】PHP7Swoole

    摘要:但在密集計算方面比等靜態編譯語言差幾十倍甚至上百倍。一使用棧內存在引擎和擴展中,經常要創建一個的變量,底層就是一個指針。代碼中創建的變量也進行了優化,直接在棧內存上預分配。應用層與底層在錯誤拋出的方式全部統一為異常。 原文:http://rango.swoole.com/archives/440最近PHP官方終于發布了傳說中的PHP7,雖然只是alpha版。PHP7號稱是新一代的PHP...

    MingjunYang 評論0 收藏0
  • foreach遍歷過程中的奇怪現象(PHP5)

    摘要:中基礎中的三大坑,遍歷,引用機制,數組。今天我們在講講中的一些奇怪現象。本文適合有一定基礎的。運行流程共用一個結構體開始遍歷數組,進行判斷,拷貝數組是一個新的結構體,操作的是新的結構體。那么遍歷數組時,全程與原數組無關。 PHP中基礎中的三大坑,foreach遍歷,引用機制&,數組。 今天我們在講講foreach中的一些奇怪現象。 在講解之前,可以先看看我其他相關的文章,屬于同一個大的...

    kgbook 評論0 收藏0
  • (PHP7內核剖析-11) 模塊擴展

    摘要:編譯工具這個腳本主要生成了編譯需要的配置以及擴展的基本結構這個腳本主要是獲取的安裝信息用于生成文件編寫擴展的基本步驟通過目錄下腳本生成擴展的基本框架修改配置設置編譯配置參數設置擴展的源文件依賴庫函數檢查等等定義一個這樣的編譯參數 1. 編譯工具 (a).ext_skel:這個腳本主要生成了編譯需要的配置以及擴展的基本結構 (b).php-config:這個腳本主要是獲取PHP的安裝信息...

    gplane 評論0 收藏0
  • PHP7擴展開發(五):回調php函數開發一個并行擴展

    摘要:起步很多時候,需要把控制權限交給用戶,或者在擴展里完成某件事后去回調用戶的方法。在擴展里是通過函數來調用用戶空間的函數的。收集回調函數的返回值。回調函數需要傳遞參數的個數。利用語言多線程庫來實現一個簡單的并行擴展。 起步 很多時候,需要把控制權限交給用戶,或者在擴展里完成某件事后去回調用戶的方法。 在PHP擴展里是通過 call_user_function_ex 函數來調用用戶空間的函...

    _ang 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<