# 變量存儲
## 參考
[引用計數基本知識](https://www.php.net/manual/zh/features.gc.refcounting-basics.php)
[PHP的垃圾回收機制](https://www.cnblogs.com/xuxubaobao/p/10840176.html)
[# PHP內核探索之變量---變量的容器-Zval](https://blog.csdn.net/ohmygirl/article/details/41542445)
## 1.變量存儲
PHP變量存在于一個叫"Zval是"的變量容器中。
Zval是PHP中最重要的數據結構之一 ,它包含了PHP中的變量值和類型的相關信息。它是一個struct基本結構。
zval 的結構體如下:
~~~
struct _zval_struct {
zvalue_value value; /* 變量值 */
zend_uint refcount__gc; /* 變量個數 */
zend_uchar type; /* 變量類型 */
zend_uchar is_ref__gc; /* 引用標志 */
};
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**
**refcount**實際上是一個計數器,用來保存有多少變量(或者符號,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等調試工具查看。
## 2.信息查看
~~~php
$str = "test zval";
xdebug_debug_zval('str');
~~~
輸出結果:
~~~
str: (refcount=1, is_ref=0)='test zval'
~~~