这两个东西长得很像,起初我还错误以为一个是针对zval一个针对zval*的释放函数,唉,太天真了。这两个函数都与zval的释放有关,是我们肯定会经常碰到的两个函数。


下面是两者的声明:

//zval_dtor是宏函数,最终展开后
ZEND_API   void  _zval_dtor_func(zval *zvalue ZEND_FILE_LINE_DC)

//zval_ptr_dtor是宏函数,最终展开后
ZEND_API  void  _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC)

两者的工作都与释放zval有关,但又有很大的区别。

比如我们有一个zval *tmp,而且我们已经对它进行了MAKE_STD_ZVAL等一系列操作了。


如果我们对它使用zval_dtor会发生什么事情呢?

zval_dtor会直接把我们的tmp的value部分即tmp->value所指的内存释放掉。当然也可能不是直接释放掉,而是重新交给ZendMM,由ZendMM进行重新分配或者释放。


如果我们对它使用zval_ptr_dtor会发生什么事情呢?

zval_ptr_dtor首先会将它的refcount减一,如果减一后refcount为0了,便会再调用zval_dtor把tmp->value给释放掉,然后再调用efree_rel()函数把自己tmp所指的zval类型结构体所占的内存空间给释放掉。


如果减一后不为0呢?那zval_ptr_dtor便不会释放tmp->value和tmp本身,而是通知一下GC垃圾回收器,然后返回而已。这里你可能要问了,它通知GC垃圾回收器干嘛,难道有基情?这个具体的细节我也说不清楚,只能从功能上来描述一下。


PHP文档中有这么一句话:”首先,我们先要建立一些基本规则,如果一个引用计数增加,它将继续被使用,当然就不再在垃圾中。如果引用计数减少到零,所在变量容器将被清除(free)。就是说,仅仅在引用计数减少到非零值时,才会产生垃圾周期(garbage cycle)。其次,在一个垃圾周期中,通过检查引用计数是否减1,并且检查哪些变量容器的引用次数是零,来发现哪部分是垃圾。”.


引用计数减少到零的时候,zval_ptr_dtor便会调用zval_dtor与efree_rel将tmp->value与tmp本身都彻底真正的释放掉了,所以不会有什么问题。但是如果减1后不为0,便会产生一个垃圾周期。每10000[硬编码在源码里的一个值]个垃圾周期便会激活一次真正的垃圾回收机制。增加这一步的原因是因为以前php用到的引用计数内存机制,无法处理循环的引用内存泄漏。


关于这个机制的核心算法是一个27页的pdf文档,下载地址:

http://www.research.ibm.com/people/d/dfb/papers/Bacon01Concurrent.pdf。


结论是什么呢?

当我们不再需要tmp的时候,应该使用zval_ptr_dtor(&tmp);