亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > PHP > 正文

PHP內核探索之變量(1)Zval

2020-03-22 19:46:38
字體:
來源:轉載
供稿:網友
  • 作為數據的容器,我們常常需要跟變量打交道,不管這個變量是數字、數組、字符串、對象還是其他,因而可以說變量是構成語言的不可或缺的基礎。本文是PHP內核探索之變量的第一篇,主要介紹zval的基本知識,包括如下幾個方面的內容:

    Zval的基本結構 查看zval的方法:debug_zval_dump和xdebug Zval的原理,COW等

    由于寫作倉促,難免會有錯誤,歡迎指出。

    一、Zval的基本結構

    Zval是PHP中最重要的數據結構之一(另一個比較重要的數據結構是hash table),它包含了PHP中的變量值和類型的相關信息。它是一個struct,基本結構為:

    struct _zval_struct {    zhtml' target='_blank'>value_value value;     /* value */    zend_uint refcount__gc;  /* variable ref count */    zend_uchar type;          /* active type */    zend_uchar is_ref__gc;    /* if it is a ref variable */};typedef struct _zval_struct zval;

    其中:

    1.  zval_value value

    變量的實際值,具體來說是一個zvalue_value的聯合體(union):

    typedef union _zvalue_value {    long lval;                  /* long value */    double dval;                /* double value */    struct {                    /* string */        char *val;        int len;    } str;    HashTable *ht;              /* hash table value,used for array */    zend_object_value obj;      /* object */} zvalue_value;

    2.  zend_uint refcount__gc

    該值實際上是一個計數器,用來保存有多少變量(或者符號,symbols,所有的符號都存在符號表(symble table)中, 不同的作用域使用不同的符號表,關于這一點,我們之后會論述)指向該zval。在變量生成時,其refcount=1,典型的賦值操作如$a = $b會令zval的refcount加1,而unset操作會相應的減1。在PHP5.3之前,使用引用計數的機制來實現GC,如果一個zval的refcount較少到0,那么Zend引擎會認為沒有任何變量指向該zval,因此會釋放該zval所占的內存空間。但,事情有時并不會那么簡單。后面我們會看到,單純的引用計數機制無法GC掉循環引用的zval,即使指向該zval的變量已經被unset,從而導致了內存泄露(Memory Leak)。

    3.  zend_uchar type

    該字段用于表明變量的實際類型。在開始學習PHP的時候,我們已經知道,PHP中的變量包括四種標量類型(bool,int,float,string),兩種復合類型(array, object)和兩種特殊的類型(resource 和NULL)。在zend內部,這些類型對應于下面的宏(代碼位置 phpsrc/Zend/zend.h):

    #define IS_NULL     0#define IS_LONG     1#define IS_DOUBLE   2#define IS_BOOL     3#define IS_ARRAY    4#define IS_OBJECT   5#define IS_STRING   6#define IS_RESOURCE 7#define IS_CONSTANT 8#define IS_CONSTANT_ARRAY   9#define IS_CALLABLE 10

    4.  is_ref__gc

    這個字段用于標記變量是否是引用變量。對于普通的變量,該值為0,而對于引用型的變量,該值為1。這個變量會影響zval的共享、分離等。關于這點,我們之后會有論述。

    正如名字所示,ref_count__gc和is_ref__gc是PHP的GC機制所需的很重要的兩個字段,這兩個字段的值,可以通過xdebug等調試工具查看。

    二、xdebug的安裝配置

    xdebug是一個開源的PHP 性能分析和debug工具。雖然對于一般的程序調試,var_dump,echo,print,debug_backtrace等常見的調試工具已經基本夠用,但對于一些復雜的調試和性能測試,xdebug絕對是一個很好的幫手(其他的如Xhprof等工具也很優秀)。

    本文的基本環境:

    安裝xdebug的基本過程為(實際上是源碼編譯一個擴展):

    1.  下載源碼包.

      下載地址為:http://www.xdebug.org/docs/install

      本文中下載的版本為:xdebug-2.6.tar.gz

    2.  解壓

    tar xvzf xdebug-2.6.tar.gz

    3.  在xdebug的目錄執行phpize

    4.  ./configure 配置

    5.  Make&& make install

    這會生成xdebug.so擴展文件(zend_extension),位置在xdebug/modules

    6.  在php.ini中加載xdebug擴展

    zend_extension=your-xdebug-path/xdebug.so

    7.  添加xdebug的配置

    xdebug.profiler_enable = onxdebug.default_enable = onxdebug.trace_output_dir='/tmp/xdebug'xdebug.trace_output_name = trace.%c.%pxdebug.profiler_output_dir='/tmp/xdebug'xdebug.profiler_output_name='cachegrind.out.%s'

    這里不再詳細介紹各個配置項的含義,詳細的請看:http://www.xdebug.org/docs/all

    現在,PHP中,應該已經有了Xdebug的擴展信息(php –m,也可以phpinfo()):

    現在,你的腳本中,可以通過xdebug_debug_zval打印Zval的信息:

    <?php    $a = array( 'test' );    $a[] = &$a;    xdebug_debug_zval( 'a' );
    3.  Zval的更多原理

    (注,本部分主要參考:http://derickrethans.nl/collecting-garbage-phps-take-on-variables.html, 作者Derick Rethans是一位優秀的PHP內核專家,在全世界做過多次報告,都有相關的pdf下載,這里(http://derickrethans.nl/talks.html )有作者每次演講的記錄,很多都值得我們深入去學習研究)

    前面我們已經說過,PHP使用Zval這種結構來保存變量,這里我們將繼續追蹤zval的更多細節。

    1. 創建變量時,會創建一個zval.

    $str = 'test zval';xdebug_debug_zval('str');

    輸出結果:

    str: (refcount=1, is_ref=0)='test zval'

    當使用$str='test zval';來創建變量時,會在當前作用域的符號表中插入新的符號(str),由于該變量是一個普通的變量,因此會生成一個refcount=1且is_ref=0的zval容器。也就是說,實際上是這樣的:

    2. 變量賦值給另外一個變量時,會增加zval的refcount值。

    $str  = 'test zval';$str2 = $str;xdebug_debug_zval('str');xdebug_debug_zval('str2');

    輸出結果:

    str: (refcount=2, is_ref=0)='test zval'str2: (refcount=2, is_ref=0)='test zval'

    同時我們看到,str和是str2這兩個symbol的zval結構是一樣的。這里其實是PHP所做的一個優化,由于str和str2都是普通變量,因而它們指向了同一個zval,而沒有為str2開辟單獨的zval。這么做,可以在一定程度上節省內存。這時的str,str2與zval的對應關系是這樣的:

    3. 使用unset時,對減少相應zval的refcount值
    $str  = 'test zval';$str3 = $str2 = $str;xdebug_debug_zval('str');unset($str2,$str3)xdebug_debug_zval('str');

    結果為:

    str: (refcount=3, is_ref=0)='test zval'str: (refcount=1, is_ref=0)='test zval'

    由于unset($str2,$str3)會將str2和str3從符號表中刪除,因此,在unset之后,只有str指向該zval,如下圖所示:

    現在如果執行unset($str),則由于zval的refcount會減少到0,該zval會從內存中清理。這當然是最理想的情況。

    但是事情并不總是那么樂觀。

    4. 數組變量與普通變量生成的zval非常類似,但也有很大不同

    與標量這些普通變量不同,數組和對象這類復合型的變量在生成zval時,會為每個item項生成一個zval容器。例如:

    $ar = array(    'id'   => 38,    'name' => 'shine'); xdebug_debug_zval('ar');

    打印出zval的結構是:

    ar: (refcount=1, is_ref=0)=array (    'id' => (refcount=1, is_ref=0)=38,     'name' => (refcount=1, is_ref=0)='shine')

    如下圖所示:

    可以看出,變量$ar生成的過程中,共生成了3個zval容器(紅色部分標注)。對于每個zval而言,refcount的增減規則與普通變量的相同。例如,我們在數組中添加另外一個元素,并把$ar['name']的值賦給它:

    $ar = array(    'id'   => 38,    'name' => 'shine');$ar['test'] = $ar['name'];xdebug_debug_zval('ar');

    則打印出的zval為:

    ar: (refcount=1, is_ref=0)=array (    'id' => (refcount=1, is_ref=0)=38,    'name' => (refcount=2, is_ref=0)='shine',    'test' => (refcount=2, is_ref=0)='shine')

    如同普通變量一樣,這時候,name和test這兩個symbol指向同一個zval:

    同樣的,從數組中移除元素時,會從符號表中刪除相應的符號,同時減少對應zval的refcount值。同樣,如果zval的refcount值減少到0,那么就會從內存中刪除該zval:

    $ar = array(    'id'   => 38,    'name' => 'shine');$ar['test'] = $ar['name'];unset($ar['test'],$ar['name']);xdebug_debug_zval('ar');

    輸出結果為:

    ar: (refcount=1, is_ref=0)=array ('id' => (refcount=1, is_ref=0)=38)

    5. 引用的出現,會令zval的規則變得復雜

    在加入引用之后,情況會變的稍微復雜一點。例如,在數組中添加對本身的引用:

    $a = $array('one');$a[] = &$a;xdebug_debug_zval('a');

    輸出的結果:

    a: (refcount=2, is_ref=1)=array (    0 => (refcount=1, is_ref=0)='one',     1 => (refcount=2, is_ref=1)=...)

    上述輸出中,…表示指向原始數組,因而這是一個循環的引用。如下圖所示:

    現在,我們對$a執行unset操作,這會在symbol table中刪除相應的symbol,同時,zval的refcount減1(之前為2),也就是說,現在的zval應該是這樣的結構:

    (refcount=1, is_ref=1)=array (    0 => (refcount=1, is_ref=0)='one',     1 => (refcount=1, is_ref=1)=...)

    也就是下圖所示的結構:

      這時,不幸的事情發生了!

      Unset之后,雖然沒有變量指向該zval,但是該zval卻不能被GC(指PHP5.3之前的單純引用計數機制的GC)清理掉,因為zval的refcount均大于0。這樣,這些zval實際上會一直存在內存中,直到請求結束(參考SAPI的生命周期)。在此之前,這些zval占據的內存不能被使用,便白白浪費了,換句話說,無法釋放的內存導致了內存泄露。

      如果這種內存泄露僅僅發生了一次或者少數幾次,倒也還好,但如果是成千上萬次的內存泄露,便是很大的問題了。尤其在長時間運行的腳本中(例如守護程序,一直在后臺執行不會中斷),由于無法回收內存,最終會導致系統“再無內存可用”。

    6. zval分離(Copy on write和change on write)

    前面我們已經介紹過,在變量賦值的過程中例如$b = $a,為了節省空間,并不會為$a和$b都開辟單獨的zval,而是使用共享zval的形式:

    那么問題來了:如果其中一個變量發生變化時,如何處理zval的共享問題?

    對于這樣的代碼:

    $a = 'a simple test';$b = $a;echo 'before write:'.PHP_EOL;xdebug_debug_zval('a');xdebug_debug_zval('b');$b = 'thss';echo 'after write:'.PHP_EOL;xdebug_debug_zval('a');xdebug_debug_zval('b');

    打印的結果是:

    before write:a: (refcount=2, is_ref=0)='a simple test'b: (refcount=2, is_ref=0)='a simple test'after write:a: (refcount=1, is_ref=0)='a simple test'b: (refcount=1, is_ref=0)='thss'

    起初,符號表中a和b指向了同一個zval(這么做的原因是節省內存),而后$b發生了變化,Zend會檢查b指向的zval的refcount是否為1,如果是1,那么說明只有一個符號指向該zval,則直接更改zval。否則,說明這是一個共享的zval,需要將該zval分離出去,以保證單獨變化互不影響,這種機制叫做COW –Copy on write。在很多場景下,COW都是一種比較高效的策略。

    那么對于引用變量呢?

    $a = 'test';$b = &$a;echo 'before change:'.PHP_EOL;xdebug_debug_zval('a');xdebug_debug_zval('b');$b = 12;echo 'after change:'.PHP_EOL;xdebug_debug_zval('a');xdebug_debug_zval('b');unset($b);echo 'after unset:'.PHP_EOL;xdebug_debug_zval('a');xdebug_debug_zval('b');

    輸出的結果為:

    before change:a: (refcount=2, is_ref=1)='test'b: (refcount=2, is_ref=1)='test'after change:a: (refcount=2, is_ref=1)=12b: (refcount=2, is_ref=1)=12after unset:a: (refcount=1, is_ref=0)=12

    可以看出,在改變了$b的值之后,Zend會檢查zval的is_ref檢查是否是引用變量,如果是引用變量,則直接更改即可,否則,需要執行剛剛提到的zval分離。由于$a 和 $b是引用變量,因而更改共享的zval實際上也間接更改了$a的值。而在unset($b)之后,變量$b從符號表中刪除了。

    這里也說明一個問題,unset并不是清除zval,而只是從符號表中刪除相應的symbol。這樣一來,之前很多的關于引用的疑問也可以理解了(下一節我們將深入探索PHP的引用)。

    PHP編程

    鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

  • 發表評論 共有條評論
    用戶名: 密碼:
    驗證碼: 匿名發表
    亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
    国产男人精品视频| 精品视频久久久久久久| 国产精品伦子伦免费视频| 成人观看高清在线观看免费| 国产精品美女久久久免费| 在线精品国产成人综合| 国产一区玩具在线观看| 欧美视频在线观看免费| 91在线播放国产| 欧美激情欧美激情在线五月| 国产丝袜一区二区三区免费视频| 国产精品人成电影在线观看| 亚洲自拍偷拍视频| 国产精品久久婷婷六月丁香| 国产免费一区二区三区在线观看| 亚洲成人三级在线| 伦伦影院午夜日韩欧美限制| 91久久嫩草影院一区二区| 在线精品播放av| 91国语精品自产拍在线观看性色| 亚洲视频欧洲视频| 亚洲精品videossex少妇| 久久夜精品va视频免费观看| 日韩性生活视频| 久久久av网站| 中文字幕欧美专区| 一本色道久久综合亚洲精品小说| 亚洲国产中文字幕在线观看| 久久理论片午夜琪琪电影网| 国产美女高潮久久白浆| 国内精品在线一区| 国产精品久久久久久久久久久久久| 这里只有精品视频在线| 岛国av在线不卡| 午夜精品99久久免费| 97涩涩爰在线观看亚洲| 青青草精品毛片| 日韩有码在线观看| 日韩av在线网址| 美女福利精品视频| 欧美激情久久久久久| 日韩电影中文字幕av| 亚洲第一中文字幕在线观看| 91丝袜美腿美女视频网站| 欧美人在线视频| 亚洲国产欧美一区二区丝袜黑人| 69**夜色精品国产69乱| 日韩av在线一区二区| 亚洲第一中文字幕| 欧美日韩免费看| 欧美日韩国产中文字幕| 中文字幕av一区中文字幕天堂| 97在线精品国自产拍中文| 亚洲自拍偷拍区| 国产成人精品视频在线| 日韩精品免费综合视频在线播放| 九九热最新视频//这里只有精品| 久热精品在线视频| 日韩av网站大全| 在线视频国产日韩| 成人精品视频久久久久| 亚洲激情在线观看视频免费| 久久精品国产欧美激情| 国产成人精品优优av| 亚洲精品wwwww| 亚洲性日韩精品一区二区| 国产成人福利视频| 亚洲图片欧美午夜| 欧美片一区二区三区| 亚洲系列中文字幕| 宅男66日本亚洲欧美视频| 色综合伊人色综合网站| 欧美激情精品久久久久久蜜臀| 欧美黑人极品猛少妇色xxxxx| 日韩精品视频在线免费观看| 在线丨暗呦小u女国产精品| 精品国内产的精品视频在线观看| 国产欧美日韩视频| 色综合久久88色综合天天看泰| 亚洲精品国精品久久99热一| 欧美日韩免费观看中文| 亚洲欧美国产视频| 国产精品久久综合av爱欲tv| 久热精品视频在线观看一区| 欧洲中文字幕国产精品| 日韩精品久久久久久福利| 精品国产乱码久久久久久天美| 国产精品三级网站| 久久久99久久精品女同性| 日韩欧美亚洲综合| 亚洲第一免费网站| 久久久久久久久久久91| 欧美一级视频免费在线观看| 91精品啪aⅴ在线观看国产| 欧美精品第一页在线播放| 揄拍成人国产精品视频| 国内精品400部情侣激情| 精品亚洲精品福利线在观看| 亚洲成人精品久久| 91中文精品字幕在线视频| 91色p视频在线| 亚洲精品久久在线| 欧美整片在线观看| 国产精品va在线| 亚洲精品999| 亚洲美女av在线播放| 久久91精品国产| 欧美性xxxxx极品| 亚洲电影在线看| 98精品国产高清在线xxxx天堂| 日韩av片免费在线观看| 欧美激情欧美狂野欧美精品| 日韩精品一区二区视频| 日韩性xxxx爱| 免费不卡在线观看av| 欧美亚洲在线观看| 欧美视频在线观看免费网址| 91精品久久久久久久久久久久久| 国产成人精品久久二区二区91| 国产欧美精品久久久| 91影视免费在线观看| 国产精品久久久久av免费| 亚洲欧美精品伊人久久| 97在线观看免费| 亚洲欧美在线看| 国产精品亚洲片夜色在线| 亚洲区在线播放| 亚洲男人天堂2023| 成年人精品视频| 亚洲精品v欧美精品v日韩精品| 日韩av免费在线看| 青青草原一区二区| 久久亚洲综合国产精品99麻豆精品福利| 亚洲综合在线小说| 欧美性猛交xxxx免费看久久久| 日韩精品中文字幕在线| 波霸ol色综合久久| 欧美激情在线视频二区| 欧美激情亚洲精品| 日韩二区三区在线| 国产精品国产三级国产aⅴ9色| 亚洲欧美国产制服动漫| 欧美日韩一区二区在线播放| 欧美午夜激情视频| 国语自产精品视频在免费| 中文字幕在线亚洲| 疯狂做受xxxx高潮欧美日本| 久久精品国产69国产精品亚洲| 亚洲国产高清福利视频| 性亚洲最疯狂xxxx高清| 91精品久久久久久综合乱菊| 俺也去精品视频在线观看| 久久亚洲一区二区三区四区五区高| 欧美日韩国产综合视频在线观看中文| 超碰日本道色综合久久综合| 欧美视频不卡中文| 亚洲老头老太hd| 国产精品旅馆在线| 97在线视频免费播放| 狠狠久久亚洲欧美专区| 日韩精品在线免费| 欧美性xxxx| 国产精品久久久久久av福利| 国产狼人综合免费视频|