深入理解 PHP7 中全新的 zval 容器和引用計數機制

深入理解 PHP7 中全新的 zval 容器和引用計數機制

最近在查閱 PHP7 垃圾回收的資料的時候,網上的一些代碼示例在本地環境下運行時出現了不同的結果,使我一度非常迷惑。 仔細一想不難發現問題所在:這些文章大多是 PHP5.x 時代的,而 PHP7 發布后,采用了新的 zval 結構,相關的資料也比較貧瘠,所以我結合一些資料做了一個總結,?主要側重于解釋新 zval 容器中的引用計數機制?,如有謬誤,還望不吝指教。

PHP7 中新的 zval 結構

明人不說暗話,先看代碼!


  1. struct?_zval_struct{??
  2. union?{??
  3. zend_long?????lval;???????/*?long?value?*/??
  4. double??????dval;???????/*?double?value?*/??
  5. zend_refcounted?*counted;??
  6. zend_string???*str;??
  7. zend_array????*arr;??
  8. zend_object???*obj;??
  9. zend_resource??*res;??
  10. zend_reference*ref;??
  11. zend_ast_ref???*ast;??
  12. zval???????*zv;??
  13. void???????*ptr;??
  14. zend_class_entry?*ce;??
  15. zend_function??*func;??
  16. struct?{??
  17. uint32_t?w1;??
  18. uint32_t?w2;??
  19. }?ww;??
  20. }?value;??
  21. union?{??
  22. struct?{??
  23. ZEND_ENDIAN_LOHI_4(??
  24. zend_uchar??type,?????/*?active?type?*/??
  25. zend_uchar??type_flags,??
  26. zend_uchar??const_flags,??
  27. zend_uchar??reserved)???/*?call?info?for?EX(This)?*/??
  28. }?v;??
  29. uint32_t?type_info;??
  30. }?u1;??
  31. union?{??
  32. uint32_t???var_flags;??
  33. uint32_t???next;?????????/*?hash?collision?chain?*/??
  34. uint32_t???cache_slot;??????/*?literal?cache?slot?*/??
  35. uint32_t???lineno;????????/*?line?number?(for?ast?nodes)?*/??
  36. uint32_t???num_args;???????/*?arguments?number?for?EX(This)?*/??
  37. uint32_t???fe_pos;????????/*?foreach?position?*/??
  38. uint32_t???fe_iter_idx;?????/*?foreach?iterator?index?*/??
  39. }?u2;??
  40. };??

對于該結構的詳細描述可以參考文末鳥哥的文章,寫的非常詳細,我就不關公面前耍大刀了,這里我只提出幾個比較關鍵的點:

  1. PHP7 中的變量分為 變量名 和 變量值 兩部分,分別對應 zval_struct和在其中聲明的 value
  2. zval_struct.value中的 zend_long 、 double 都是 簡單數據類型 ,能夠直接儲存具體的值,而其他復雜數據類型儲存一個指向其他數據結構的 指針
  3. PHP7 中,引用計數器儲存在 value 中而不是 zval_struct
  4. NULL 、 布爾型 都屬于 沒有值 的數據類型(其中布爾型通過 IS_FALSE 和 IS_TRUE 兩個常量來標記),自然也就沒有引用計數
  5. 引用 (REFERENCE)變為了一種數據結構而不再只是一個標記位了,它的結構如下:

  1. struct?_zend_reference{??
  2. zend_refcounted_h?gc;??
  3. zval???????val;??
  4. }??

6.

zend_reference作為?zval_struct中包含的一種 value 類型,也擁有自己的 val 值,這個值是指向一個?zval_struct.value的。他們都擁有自己的 引用計數器 。

?

引用計數器用來記錄當前有多少 zval 指向同一個 zend_value 。

針對第六點,請看如下代碼:


  1. $a?=?'foo';??
  2. $b?=?&$a;??
  3. $c?=?$a;??

此時的數據結構是這樣的:

$a 與 $b 各擁有一個 zval_struct容器,并且其中的 value 都指向同一個

zend_reference結構,?zend_reference內嵌一個 val 結構, 指向同一個 zend_string , 字符串的內容 就儲存在其中。

?

而 $c 也擁有一個 zval_struct,而它的 value 在初始化的時候可以直接指向上面提到的 zend_string ,這樣在拷貝時就不會產生復制。

下面我們就聊一聊在這種全新的 zval 結構中,會出現的種種現象,和這些現象背后的原因。

問題

一. 為什么某些變量的引用計數器的初始值為 0

現象


  1. $var_int?=?233;??
  2. $var_float?=?233.3;??
  3. $var_str?=?'233';??
  4. xdebug_debug_zval('var_int');??
  5. xdebug_debug_zval('var_float');??
  6. xdebug_debug_zval('var_str');??
  7. /**?輸出?**?
  8. var_int:?
  9. (refcount=0,?is_ref=0)int?233?
  10. var_float:?
  11. (refcount=0,?is_ref=0)float?233.3?
  12. var_str:?
  13. (refcount=0,?is_ref=0)string?'233'?(length=3)?
  14. **********/??

原因

在 PHP7 中,為一個變量賦值的時候,包含了兩部分操作:

  1. 為符號量(即變量名)申請一個 zval_struct結構
  2. 將變量的值儲存到 zval_struct.value中 對于 zval 在 value 字段中能保存下的值,就不會在對他們進行引用計數, 而是在拷貝的時候直接賦值 ,這部分類型有:
  • IS_LONG
  • IS_DOUBLE

即我們在 PHP 中的 整形 與 浮點型 。

那么 var_str 的 refcount 為什么也是 0 呢?

這就牽扯到 PHP 中字符串的兩種類型:

1.interned string 內部字符串(函數名、類名、變量名、靜態字符串):

?

$str = '233';??? // 靜態字符串

?

2.普通字符串:

?

$str = '233' . time();

?

對于 內部字符串 而言,字符串的內容是唯一不變的,相當于 C 語言中定義在靜態變量區的字符串, 他們的生存周期存在于整個請求期間,request 完成后會統一銷毀釋放 ,自然也就無需通過引用計數進行內存管理。

二. 為什么在對整形、浮點型和靜態字符串型變量進行引用賦值時,計數器的值會直接變為2

現象


  1. $var_int_1?=?233;??
  2. $var_int_2?=?&var_int;??
  3. xdebug_debug_zval('var_int_1');??
  4. /**?輸出?**?
  5. var_int:?
  6. (refcount=2,?is_ref=1)int?233?
  7. **********/??

原因

回憶一下我們開頭講的 zval_struct中 value 的數據結構,當為一個變量賦 整形 、 浮點型 或 靜態字符串 類型的值時,value 的數據類型為 zend_long 、 double 或 zend_string ,這時值是可以直接儲存在 value 中的。而按值拷貝時,會開辟一個新的 zval_struct以同樣的方式將值儲存到相同數據類型的 value 中,所以 refcount 的值一直都會為 0。

但是當使用 & 操作符進行引用拷貝時,情況就不一樣了:

  1. PHP 為 & 操作符操作的變量申請一個 zend_reference結構
  2. 將 zend_reference.value 指向原來的 zval_struct.value
  3. zval_struct.value的數據類型會被修改為 zend_refrence
  4. 將 zval_struct.value指向剛剛申請并初始化后的 zend_reference
  5. 為新變量申請 zval_struct結構,將他的 value 指向剛剛創建的 zend_reference

此時: var_int_2 都擁有一個 zval_struct結構體,并且他們的 zval_struct.value都指向了同一個 zend_reference結構,所以該結構的引用計數器的值為 2。

題外話:zend_reference又指向了一個整形或浮點型的 value,如果指向的 value 類型是 zend_string,那么該 value 引用計數器的值為 1。而 xdebug 出來的 refcount 顯示的是 zend_reference的計數器值(即 2)

三. 為什么初始數組的引用計數器的值為 2

現象


  1. $var_empty_arr?=?[1,?2,?'3'];??
  2. xdebug_debug_zval('var_empty_arr');??
  3. /**?輸出?**?
  4. var_arr:?
  5. (refcount=3,?is_ref=0)?
  6. array?(size=3)?
  7. 0?=>?(refcount=0,?is_ref=0)int?1?
  8. 1?=>?(refcount=0,?is_ref=0)int?2?
  9. 2?=>?(refcount=1,?is_ref=0)string?'3'?(length=1)?
  10. **********/??

原因

這牽扯到 PHP7 中的另一個概念,叫做 immutable array (不可變數組)。 關于 immutable array 的詳細介紹我放到下篇文章中講,這里我們只需要知道,這樣定義的數組,叫做 不可變數組 。

For arrays the not-refcounted variant is called an "immutable array". If you use opcache, then constant array literals in your code will be converted into immutable arrays. Once again, these live in shared memory and as such must not use refcounting. Immutable arrays have a dummy refcount of 2, as it allows us to optimize certain separation paths.

不可變數組和我們上面講到的 內部字符串 一樣,都是 不使用引用計數 的,但是不同點是,內部字符串的計數值恒為 0,而不可變數組會使用一個 偽計數值 2。

?

參考文章:

  • 深入解析PHP的引用計數機制
  • PHP 應用容器化以及部署方法
  • PHP解耦的三重境界(淺談服務容器)

轉載于:https://www.cnblogs.com/sohuhome/p/9800977.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/450996.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/450996.shtml
英文地址,請注明出處:http://en.pswp.cn/news/450996.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

分布式系統的架構思路

見:http://www.cnblogs.com/chulung/p/5653135.html 一、前言 在計算機領域,當單機性能達到瓶頸時,有兩種方式可以解決性能問題,一是堆硬件,進一步提升配置,二是分布式,水平擴展。當然&#xff…

狂賭智能手機 中國互聯網巨頭深陷零利潤困局

編者按:智能手機正在中國普及,互聯網企業趨之若鶩。然而,在蘋果、三星共享智能手機市場99%利潤的大背景下,中國互聯網企業要從所剩無幾的利潤空間里分一杯羹,注定備受煎熬,前路迷茫。 互聯網巨頭紛紛進入智…

占用較多堆外內存的區域

(1)Director Memory 主要在nio中會使用,在內存不足時會拋出OOM或者OOM:Direct buffer memory。 (2)線程堆棧 為每個線程分配的棧空間,用于保存局部變量,執行程序代碼。內存不足時可能拋出StackO…

Oracle SELECT INTO 和 INSERT INTO SELECT 兩種表復制語句詳解

在Oracle中select into from不可以使用,用create table select代替該功能!!!在Sql Server中可以正常使用。1.INSERT INTO SELECT語句語句形式為:Insert into Table2(field1,field2,...) select value1,value2,... from…

帆軟地址欄傳參,實例

自動查詢: http://help.finereport.com/finereport9.0/doc-view-409.html參數的種類與區別: http://help.finereport.com/doc-view-156基本參數傳遞(視頻): http://bbs.fanruan.com/lesson-14.html超級鏈接-傳遞多個值…

RMI 說明

見:https://baike.baidu.com/item/RMI/1786244?fraladdin RMI遠程方法調用 相關概述 RMI是Java的一組擁護開發分布式應用程序的API。RMI使用Java語言接口定義了遠程對象,它集合了Java序列化和Java遠程方法協議(Java Remote Method Protocol)。簡單地說&…

李善友:為什么外企人不敢創業

摘要:20年前,人們最驕傲的是進外企,創業意味著找不到工作。而現在相反,你要說自己在外企工作,會被人笑話,令人激動的事兒是去創業。 李善友:中歐創業中心主任創業學兼任教授、酷6網創始人 孫陶然…

JVM對象占用內存計算

大家都知道,jvm中對象實例存儲在堆中,對象的引用存儲在棧中,而對象的元數據(類型數據)存儲在方法區。在我們進行內存優化的過程中經常需要了解每個對象占用的內存大小。接下來我將介紹對象占用內存大小的計算方式。 Java的對象模型 java是面…

繪圖基礎語法與常用參數

1 # -*- coding: utf-8 -*-2 3 ###############################################################################4 ####################### 正文代碼 #######################5 #################################################################…

MyEclipse 皮膚、主題、背景色

第一步:打開myeclipse--->help--->install from site--->Add將路徑粘貼在這里。等待安裝顏色主題。https://raw.github.com/guari/eclipse-ui-theme/master/com.github.eclipseuitheme.themes.updatesite 第二步:http://eclipsecolorthemes.org…

RPC 遠程過程調用協議

RPC(Remote Procedure Call Protocol)——遠程過程調用協議,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。 RPC協議假定某些傳輸協議的存在,如TCP或UDP,為通信程序之間攜…

周鴻祎:創業前的積累很重要

摘要:雖然公司上市,也投資了很多公司,日前,在中國人民大學的演講中,周鴻祎卻稱自己“從來不是一個成功人士,曾經是一個最大的失敗者”。 360特供機還沒露面,已經被周鴻祎通過微博炒得火熱&#…

BZOJ 4710 [Jsoi2011]分特產 解題報告

4710 [Jsoi2011]分特產 題意 給定\(n\)個集合,每個集合有相同的\(a_i\)個元素,不同的集合的元素不同。將所有的元素分給\(m\)個不同位置,要求每個位置至少有一個元素,求分配方案數。 先考慮兩個簡單的問題 給定\(m\)個相同元素和\…

java接口調試思想

對于接口調試的理解:最近多次參與接口調試工作,一般情況都是獲取對方接口文檔,文檔中有加密驗證方式,根據加密驗證方式開發,調用對應的接口。可以不可以簡化這個流程那,至少減少一方的工作量。1、減少調用方…

SOA (面向服務的架構)

見:https://baike.baidu.com/item/SOA/2140650?fraladdin UDDI 解說參見:UDDI是什么 SOAP解說參見: SOAP:簡單對象訪問協議 面向服務的架構(SOA)是一個組件模型,它將應用程序的不同功能單元(稱…

mysql中count(*)和count(1)和count(column)區別

在日常的mysql使用中,我們經常會看到SELECT COUNT(*)、SELECT COUNT(1)等查詢語句,他們到底有什么區別呢?今天我就來總結下。 我們先從函數的含義說起: count() 統計滿足查詢條件的結果集的總行數(包含null),其中count…

第一天筆記

編程語言分類: 1. 機器語言:用二進制指令編程,本質是直接操作硬件。 優點:執行效率高 缺點:開發效率低,學習難度高 2.匯編語言:用英文標簽代替二進制指令,本質也是直接操作硬件。…

索尼MOTO等壓榨國內代工廠:員工宿舍像監獄

摘要:據調查報告披露,偉易達血汗工廠的壓榨情況比起富士康、蘋果等有過之而無不及,包括強迫工人超負荷工作、暴露于有害化學物質、住宿環境差、虐待員工、超低的工資等。如前面保羅克魯格曼發表了《表揚廉價勞動》一文,N.D.克里斯…

[cerc2012][Gym100624B]20181013

轉載于:https://www.cnblogs.com/KonjakJuruo/p/9809637.html

Nginx服務器證書部署-亞洲誠信

Nginx服務器證書部署發布時間:2018-01-17 16:15:25依賴建議l SSL卸載驅動。建議:openssl版本1.1.0f。l nginx版本Stable version:最新穩定版,生產環境上建議使用的版本。獲取證書MPKI方式:1. 登錄https://mpki.tru…