php的垃圾回收机制主要参考了http://blog.csdn.net/phpkernel/article/details/5734743 这文章。

变量对应的值,比如 $a="abc"  abc在内核是以一个zval 的形式存在的,下面列出其zval 的定义

287 typedef struct _zval_struct zval;

307 typedef union _zvalue_value {
308 long lval; /* long value */
309 double dval; /* double value */
310 struct {
311 char *val;
312 int len;
313 } str;
314 HashTable *ht; /* hash table value */
315 zend_object_value obj;
316 } zvalue_value;

318 struct _zval_struct {
319 /* Variable information */
320 zvalue_value value; /* value */
321 zend_uint refcount__gc;
322 zend_uchar type; /* active type */
323 zend_uchar is_ref__gc;
324 };

变量$a本身也要开辟一段内存,在另外一篇文章里会讲到 变量的内存分配,这里先略过

$a="abc";

内核使用宏ALLOC_ZVAL和 INIT_ZVAL 对abc 分配一段内存,并进行初始化

因为有引号的出现,所以php内核会认为这是一个字符串,于是 zval.type=IS_STRING , zval.str.val="abc", zval.str.len=3, zval.refcount_gc=1 ,zval.is_ref_gc=0

$b=$a; 那么php内核不会为 $b再分配一段内存,避免了内存浪费,而是将$b指向了 "abc"  这个zval ,  也就是说在变量符号表(HashTable实现)中 key为b的,value 为 "abc"所在的 zval的指针,即内存地址 这里:"abc" 的 refcont_gc=2, is_ref_gc=0

$b=2; 这个时候,$b发生了变化,那么不至于影响到 $a的使用, 内核会把 "abc"所在的zval 的 refcount_gc -1, (abc的refcount_gc=1,is_ref=0 )再把abc对应的zval 复制一份,并在变量符号表中更新key为b 的value值,也就是常说的 COPY ON WRITE(写时复制)  2的refcount_gc=1 , is_ref_gc=0

$a=&$b; $a会直接指i向$b对应的zval,  值为2 的 refcount=2 , is_ref=1

unset($a); 值为2的 refcount=1 ,is_ref=1 注:unset,把变量$a本身 从符号表中删除,同时refcount_gc-1,如果减后的refcount_gc为0,则进行收回内存

如果 是 数组,或者对象可能就会出问题

$a=array(1); $a对应的zval : refcount_gc=1, is_ref=0 , $a[0]对应的zval: refcount_gc=1,is_ref=0

$a[]=&$a;   $a对应的zval : refcount_gc=2 ,is_ref=1 , $a[1] 对应的zval: refcount_gc=2, is_ref=1

unset($a);  符号表中的key='a'的变量删除了,php内核尝试将$a对应的zval:refcount_gc-1, refcount_gc结果为1,不等于0,内核则无法回收内存,这时发生了内存泄漏 (其实最终也回收了内存 ,当请求结束时,内核会回收所的内存,但是当php以daemon形式除外)

下面,php新的垃圾回收机制出场了

先看下php5.2为zval分配内存的方式

内核使用宏:MAKE_STD_ZVAL(z),该宏又包含两个宏ALLOC_ZVAL(z)和INIT_PZVAL(z)

宏ALLOC_ZVAL(z)展开后为

(z)=(zval *)emalloc(sizeof(zval)); //位于zend_alloc.h

宏INIT_PZVAL(z)展开为后://位于zend.h

(z)->refcount_gc=1;

(z)->is_ref_gc=0;

现在php5.3是用下面的方法

#undef ALLOC_ZVAL

#define ALLOC_ZVAL(z)

do{\

(z)=(zval *)emalloc(sizeof(zval_gc_info))\

GC_ZVAL_INIT(z)\

} while(0)

其中zval_gc_info是个结构体

#typedef _zval_gc_info{

  zval z;

  union{

    gc_root_buffer *buffer;

    struct _zval_gc_info *next;

  }u;

}zval_gc_info;

gc_root_buffet 也是个结构体

#typedef _gc_root_buffer{

  struct _gc_root_buffet *next;

  struct _gc_root_buffet *prev;

  union{

    zval  *pz;

  }u;

}gc_root_buffer;

还是以这个为例

$a=array(1);

$a[]=&$a;

unset($a);

php 5.3垃圾处理原理是:
1)当调用unset后,将 $a对应的zval的refcount_gc减1,这时,refcount_gc=1

2)因为1>0,zval还不能被释放 ,因为没有变量要使用zva了,unset($a),变量$a已经为空, 所以是垃圾数据,设置zval.u.buffer 这个指针的为紫色,放入一个缓冲区)

2.1)若refcount_gc=0,说明没有别的变量在使用了,可以释放了

3)当缓冲区满的时候,开始在缓冲区中遍历,将结点对应的zval的refcount_gc减1,如果节点对应的zvzl中包括的zval又指向了前面的zval(环形引用),也要refcount-1,即将zval的每一个元素都将refcount_gc减1 ,为避免重复操作,减后,将颜色置为灰色

4)再次遍历缓冲区,如果refcount_gc的值不为0,说明不是垃圾,垃圾被其他变量使用,因为将颜色为黑色,

5)如果refcount=0,说明是垃圾,设置颜色为白色,将回收内存

当缓冲区满了以后,才进行垃圾回收,因为垃圾回收也是消耗资源的,这跟生活中的垃圾箱一样,只有当垃圾箱满了以后,清洁工才过来清理垃圾。

当调用unset($a)后,内核会调用下面的方法

static zend_always_inline void i_zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC)
{
  if (!Z_DELREF_P(zval_ptr)) {
    TSRMLS_FETCH();

    ZEND_ASSERT(zval_ptr != &EG(uninitialized_zval));
    GC_REMOVE_ZVAL_FROM_BUFFER(zval_ptr);
    zval_dtor(zval_ptr);
    efree_rel(zval_ptr);
} else {
    TSRMLS_FETCH();

    if (Z_REFCOUNT_P(zval_ptr) == 1) {
    Z_UNSET_ISREF_P(zval_ptr);
  }  

  GC_ZVAL_CHECK_POSSIBLE_ROOT(zval_ptr);
}
}

static zend_always_inline void _zval_dtor(zval *zvalue ZEND_FILE_LINE_DC)
{
if (zvalue->type <= IS_BOOL) {
return;
}
_zval_dtor_func(zvalue ZEND_FILE_LINE_RELAY_CC);
}

zend_variables.c

ZEND_API void _zval_dtor_func(zval *zvalue ZEND_FILE_LINE_DC)
{
switch (Z_TYPE_P(zvalue) & IS_CONSTANT_TYPE_MASK) {
case IS_STRING:
case IS_CONSTANT:
CHECK_ZVAL_STRING_REL(zvalue);
STR_FREE_REL(zvalue->value.str.val);
break;
case IS_ARRAY:
case IS_CONSTANT_ARRAY: {
TSRMLS_FETCH();

if (zvalue->value.ht && (zvalue->value.ht != &EG(symbol_table))) {
/* break possible cycles */
Z_TYPE_P(zvalue) = IS_NULL;
zend_hash_destroy(zvalue->value.ht);
FREE_HASHTABLE(zvalue->value.ht);
}
}
break;
case IS_OBJECT:
{
TSRMLS_FETCH();

Z_OBJ_HT_P(zvalue)->del_ref(zvalue TSRMLS_CC);
}
break;
case IS_RESOURCE:
{
TSRMLS_FETCH();

/* destroy resource */
zend_list_delete(zvalue->value.lval);
}
break;
case IS_LONG:
case IS_DOUBLE:
case IS_BOOL:
case IS_NULL:
default:
return;
break;
}
}

2428 ZEND_API void _efree(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
2429 {
2430 TSRMLS_FETCH();
2431
2432 if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) {
2433 AG(mm_heap)->_free(ptr);
2434 return;
2435 }
2436 _zend_mm_free_int(AG(mm_heap), ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
2437 }

2428 ZEND_API void _efree(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
2429 {
2430 TSRMLS_FETCH();
2431
2432 if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) {
2433 AG(mm_heap)->_free(ptr);
2434 return;
2435 }
2436 _zend_mm_free_int(AG(mm_heap), ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
2437 }

zend_alloc.c

php5.3新垃圾回收机制详解的更多相关文章

  1. PHP的垃圾回收机制详解

    原文:PHP的垃圾回收机制详解 最近由于使用php编写了一个脚本,模拟实现了一个守护进程,因此需要深入理解php中的垃圾回收机制.本文参考了PHP手册. 在理解PHP垃圾回收机制(GC)之前,先了解一 ...

  2. JVM的垃圾回收机制详解和调优

    JVM的垃圾回收机制详解和调优 gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都 ...

  3. java面试题之----JVM架构和GC垃圾回收机制详解

    JVM架构和GC垃圾回收机制详解 jvm,jre,jdk三者之间的关系 JRE (Java Run Environment):JRE包含了java底层的类库,该类库是由c/c++编写实现的 JDK ( ...

  4. GC垃圾回收机制详解

    JVM堆相关知识    为什么先说JVM堆?  JVM的堆是Java对象的活动空间,程序中的类的对象从中分配空间,其存储着正在运行着的应用程序用到的所有对象.这些对象的建立方式就是那些new一类的操作 ...

  5. PHP 垃圾回收机制详解

    前言:之前对PHP的GC只是了解了个大概,这次详细了解下PHP的垃圾回收机制(GC). 介于网上大部分都是PHP5.X的GC,虽然 php5 到 php7 GC部分做出的改动较小,但我觉得还是一起写下 ...

  6. Python垃圾回收机制详解

    一.垃圾回收机制 Python中的垃圾回收是以引用计数为主,分代收集为辅.引用计数的缺陷是循环引用的问题. 在Python中,如果一个对象的引用数为0,Python虚拟机就会回收这个对象的内存. #e ...

  7. JVM的内存区域划分以及垃圾回收机制详解

    在我们写Java代码时,大部分情况下是不用关心你New的对象是否被释放掉,或者什么时候被释放掉.因为JVM中有垃圾自动回收机制.在之前的博客中我们聊过Objective-C中的MRC(手动引用计数)以 ...

  8. Python垃圾回收机制详解转自--Kevin Lu

    一.垃圾回收机制 Python中的垃圾回收是以引用计数为主,分代收集为辅.引用计数的缺陷是循环引用的问题. 在Python中,如果一个对象的引用数为0,Python虚拟机就会回收这个对象的内存. #e ...

  9. Java垃圾回收机制详解和调优

    gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集 ...

随机推荐

  1. vue 开发系列(三) vue 组件开发

    概要 vue 的一个特点是进行组件开发,组件的优势是我们可以封装自己的控件,实现重用,比如我们在平台中封装了自己的附件控件,输入控件等. 组件的开发 在vue 中一个组件,就是一个独立的.vue 文件 ...

  2. nxn随机矩阵乘以概率向量依旧是概率向量

    由上面可进一步推到出A*A是随机矩阵看成(A a1,A a2...A an) 所以A^m依然是随机矩阵.

  3. MFC中的几个虚函数

    1.PreTranslateMessage()和WindowProc() PreTranslateMessage是消息在送给TranslateMessage函数之前被调用的,通过函数名也可以猜出来.绝 ...

  4. 安装了nodejs后在命令行运行npm报错:Error: Cannot find module 'internal/util/types'

    报错如下图所示: 解决方法:删除目录“C:\Users\wls\AppData\Roaming\npm\node_modules”下的npm文件夹

  5. js setInterval详解

    [自己总结]: 语法  setInterval(code,interval) ①可以有第三个参数,第三个参数作为第一个参数(函数)的参数 ②第一个参数是函数,有三种形式: 1.传函数名,不用加引号,也 ...

  6. VBA编程中的 sheet1 与 sheets(1)的区别

    [自己理解]sheet1是一个专有名词,不是任何对象的属性,只能单独使用,特指代码所在工作簿的那个sheet1(和顺序无关,是固定的一个表,sheets(1)则和顺序有关). 参考资料: 1.代码中一 ...

  7. Python-类-dict

    class dict(object): """ dict() -> new empty dictionary dict(mapping) -> new dic ...

  8. Effective C++ 随笔(3)

    条款12: 以对象管理资源 两种只能指针: std:auto_ptr<> 当使用copy操作室,原先的智能指针指向为null std:tr1:share_ptr<int> sp ...

  9. weblogic配置集群(一)

    一.程序安装 二.创建域 好的  篇幅太长  我就接下来的操作就写在下一篇博客了 weblogic配置集群(二)

  10. 20155326 2006-2007-2 《Java程序设计》第4周学习总结

    20155326 2006-2007-2 <Java程序设计>第4周学习总结 教材学习内容总结 继承共同行为 (1)继承基本上就是避免多个类间重复定义共同行为,关键词为extends. ( ...