上次跟你們講了垃圾回收機制後,有些小夥伴對底層原理比較感興趣,私信問我了一些關於變量的相關知識,既然你們對變量比較感興趣,那麼此次咱們來系統的講一下變量的底層原理php
首先,咱們仍是先擺上咱們的zval結構體,即php全部變量都會以zval結構體的形式實現數據庫
struct _zval_struct { union { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } value; //變量value值 zend_uint refcount__gc; //引用計數內存中使用次數,爲0刪除該變量 zend_uchar type; //變量類型 zend_uchar is_ref__gc; //區分是不是引用變量,是引用爲1,不然爲0 };
從上面結構體內容能夠看出每個php變量都會由變量類型
、value值
、引用計數次數
和是不是引用變量
四部分組成 segmentfault
注:上面zval結構體是php5.3版本以後,php7版本以前的結構數組
看到這裏,可能會有小夥伴們問我,php不是有8種數據類型嗎?可是爲何對應的zvalue的value值只有5種?php7
緣由是這樣的,php出於對內存節省的考慮,因此對於一些變量類型作了複用,並無一一對應去定義每一個變量類型函數
下面咱們看一下zvalue的每一個value值所對應的變量類型ui
zval.value.lval => 整型、布爾型、資源 zval.value.dval => 浮點型 zval.value.str => 字符串 zval.value.*ht => 數組 zval.value.obj => 對象
看到這裏你們可能會比較奇怪,布爾型和資源是怎麼對應到zval.value的lval上的呢?還有,NULL呢?指針
就像咱們會將true和false映射成0和1進行數據庫存儲同樣,php也是這麼作的。因此php發現zval的type值是布爾型時,會將布爾型轉成0或1存儲在zval.value的lval中code
資源對於php來講屬於一個比較特殊的變量,而php會將每一個資源對應的資源標識存儲在zval.value的lval中。常見的資源有:文件句柄、數據庫句柄等對象
對於NULL來講,就更好理解了,由於自己經過zval的type值便可區分,因此並無將NULL值存儲在zval的value中
php做爲一門動態語言,沒有先聲明變量後賦值的習慣,因此都是拿來一個變量直接就進行了賦值,那麼是如何實現的呢?
舉例:
$name = "許錚的技術成長之路";
其實每次變量被常量賦值時,都會對應生成一個變量容器。剛纔的例子會生成一個變量容器,容器的type是字符串類型,而value值則是許錚的技術成長之路
,且此時該變量容器的ref_count會加1
而變量name
是如何與變量容器關聯起來的呢?其實也是使用了php的一個內部機制,即哈希表
。每一個變量的變量名和指向zval結構的指針被存儲在哈希表
內,以此實現了變量名到變量容器的映射
上面咱們提到了變量名和變量容器映射的概念。對於php來講,變量有全局變量和局部變量之分;那麼,他們都是存儲到一個哈希表
內了麼?
其實不是的,變量存儲也有做用域的概念。全局變量被存儲到了全局符號表
內,而局部變量也就是指函數或對象內的變量,則被存儲到了活動符號表
內(每一個函數或對象都單獨維護了本身的活動符號表。活動符號表的生命週期,從函數或對象被調用時開始,到調用完成時結束)
變量銷燬,分爲如下幾種狀況:
一、手動銷燬
二、垃圾回收機制銷燬(引用計數清0銷燬和根緩衝區滿後銷燬)
咱們此次主要講一下手動銷燬,即unset,每次銷燬時都會將符號表內的變量名和對應的zval結構進行銷燬,並將對應的內存歸還到php所維護的內存池內(按內存大小劃分到對應內存列表中)
而對於垃圾回收機制的銷燬,若是你不瞭解其相關原理,那麼我建議你看下我以前寫的文章php底層原理之垃圾回收機制
今天,咱們從底層的角度,將變量從生成到銷燬講了一遍。對於變量的生成,咱們是拿常量賦值做爲示例講解的,那麼變量之間的賦值呢?是什麼原理呢?且聽下回分解~