# 变量存储
## 参考
[引用计数基本知识](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'
~~~