php 变量的分配和销毁

引用计数

引用计数是指在value中增加一个字段refcount记录指向当前value的数量,变量复制、函数传参时并不直接硬拷贝一份value数据,而是将refcount++,变量销毁时将refcount--,等到refcount减为0时表示已经没有变量引用这个value,将它销毁即可。

存储结构如下:

 1 typedef struct _zend_refcounted_h {
 2     uint32_t         refcount;            /* reference counter 32-bit */
 3     union {
 4         struct {
 5             ZEND_ENDIAN_LOHI_3(
 6                 zend_uchar    type,
 7                 zend_uchar    flags,    /* used for strings & objects */
 8                 uint16_t      gc_info)  /* keeps GC root number (or 0) and color */
 9         } v;
10         uint32_t type_info;
11     } u;
12 } zend_refcounted_h;

引用计数的信息位于给具体value结构的gc中,例如在字符串变量中:

1 typedef struct _zend_string     zend_string;
2 struct _zend_string {
3     zend_refcounted_h gc;
4     zend_ulong        h;                /* hash value */
5     size_t            len;
6     char              val[1];
7 };

写时复制

引用计数,多个变量可能指向同一个value,然后通过refcount统计引用数,这时候如果其中一个变量试图更改value的内容则会重新拷贝一份value修改,同时断开旧的指向,写时复制的机制在计算机系统中有非常广的应用,它只有在必要的时候(写)才会发生硬拷贝,可以很好的提高效率

不是所有类型都可以copy的,比如对象、资源,实时上只有string、array两种支持,与引用计数相同,也是通过zval.u1.type_flag标识value是否可复制的

3 变量回收

PHP变量的回收主要有两种:主动销毁、自动销毁。动销毁指的就是 unset。自动销毁就是PHP的自动管理机制,在return时减掉局部变量的refcount,即使没有显式的return,PHP也会自动给加上这个操作,另外一个就是写时复制时会断开原来value的指向,这时候也会检查断开后旧value的refcount。

垃圾回收

PHP变量的回收是根据refcount实现的,当unset、return时会将变量的引用计数减掉,如果refcount减到0则直接释放value,这是变量的简单gc过程

但是实际过程中出现gc无法回收导致内存泄漏的bug,先看下一个例子:

1 $a = [1];
2 $a[] = &$a;
3 unset($a);

可以看到,unset($a)之后由于数组中有子元素指向$a,所以refcount > 0,无法通过简单的gc机制回收,这种变量就是垃圾,垃圾回收器要处理的就是这种情况,目前垃圾只会出现在array、object两种类型中,所以只会针对这两种情况作特殊处理:当销毁一个变量时,如果发现减掉refcount后仍然大于0,且类型是IS_ARRAY、IS_OBJECT则将此value放入gc可能垃圾双向链表中,等这个链表达到一定数量后启动检查程序将所有变量检查一遍,如果确定是垃圾则销毁释放。

 

posted @ 2018-03-15 20:26  DearMrLi  阅读(240)  评论(0编辑  收藏  举报