PHP变量与内存关系,PHP变量和内存管理 · pigfly

zval

PHP是弱类型的语言,声明变量不需要指定类型,PHP是怎么做到的呢?

首先,所有变量都是用一个叫做zval的变量容器来保存的,结构如下:

typedef struct _zval_struct {

zvalue_value value; # 变量具体的值

zend_uint refcount; # 引用计数

zend_uchar type; # 变量类型

zend_uchar is_ref; # 是否引用

} zval;

然后value是一个联合体:

typedef union _zvalue_value {

long lval;

double dval;

struct {

char *val;

int len;

} str;

HashTable *ht;

zend_object_value obj;

} zvalue_value;

当我们给变量赋值的时候,PHP自动识别变量类型并保存到type中,然后下一次通过type来决定获取变量的方式。

但是这个zval结构并没有保存变量名称,PHP是怎么找到对应的变量的呢?

当我们创建变量的时候,php会把变量内容保存到zval,然后把变量名称和指向zval的指针保存到一个叫做符合表的东西里面,因此,PHP就可以通过变量名找到对应的zval来获取到变量值

50902839d7f7ff0daacefad7a9209200.png

那么is_ref和refcount又是干嘛的?考虑下面的代码:

$a = 'hello world';

$b = $a;

1.首先申请一块内存把变量内容保存到一个zval结构体中,然后把变量名和变量内容的映射关系保存到符合表中

2.复制变量a给变量b,这时候如果php再申请一块内存保存同样的内容到一个新的zval中,有点浪费了,所以就有了引用

那么使用引用以后,第2步是怎么处理的呢?

只需要把b保存到符号表中并且指向和a指向的同一个zval就行了,这样a和b都指向了同一个zval,然后这个zval的refcount加1,表示同时有几个变量在用到它

3ba772eb424ca0793dac035c5092c4c9.png

$a = 'hello world';

$b = $a;

unset($a);

72c69d995e8c39c2ee410a7583a11b53.png

$a = 'hello world';

$b = $a;

$a = 1;

1c684041a88c02c97db69c6fc8c1e8cb.png

$a = 'hello world';

$b = &$a;

a82f082f220dc83164cc75c2f5d1cdbe.png

$a = 'hello world';

$b = &$a;

$a = 1;

d9418fd0169783d89525729cb0b56f28.png

$a = 'hello world';

$b = $a;

$c = &$a;

273ef2e954b3d1349ed03a0492d9f48a.png

垃圾回收

通常,我们写的代码,有两个机会来做到内存的释放:首先,如果一个变量的引用计数变成0(通常是调用了unset),它所对应的zval将会直接释放(可能不是真的删除,只是释放掉符号表与zval的映射关系)

其次,在脚本执行完后PHP会释放掉。

如上所述,在PHP5.3之前,没有专门的垃圾回收,zval内存的释放只是简单地通过引用计数是否为0来进行。通过引用计数为0来销毁变量会有个问题,当数组或者对象有循环引用的情况下,当unset的时候zval refcount并没有变成0,这一部分的内存是没有办法释放掉的,会造成内存泄漏,虽然脚本执行完后PHP会释放,但是当PHP作为守护进程需要长时间运行,或者在运行过程中占用内存比较大的时候,这种机制不能有效地进行垃圾回收,造成内存占用过大。这个时候可以调用gc_collect_cycles来做到强制回收。

在PHP5.3之后,对垃圾回收有了改进,当引用计数变为0时,直接清除。当引用计数减1以后大于0时,认为其可能是垃圾,把它放入垃圾缓冲区,当缓冲区满了(默认是1w个zval)才统一进行垃圾回收。这里需要注意的是:只有减掉以后引用计数仍然大于0的zval才会被认为是垃圾,所以如果在代码中不调用unset是不会减掉引用计数的(除了变量分离的情况),那自然也就不会进行垃圾回收,只有等脚本结束才会被释放掉。执行垃圾回收的时候,PHP遍历zval的每个元素进行模拟删除,如果zval的引用计数变成0了,证明此zval是个垃圾。

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值