自嘲)。。。。。2333,我觉得这是因为在php语言层面就帮我们解决了内存回收的问题,但这让我在和java大牛们吹牛逼的时候,听到什么内存泄露。。。。(纳尼,我tmd怎么从来没遇见过)一脸懵逼。
本人小菜,如果下面所写有什么错误的地方,请大神指出,并且下文,很多都是读书+看源码之后自己的总结。
前言
在上一篇中我浅谈的PHP中的基本数据容器,zend_value,zval
实际存储数据的并不全是zend_value,还有一个被zend_value通过指针指向的具体的数据存储结构体,如_zend_array,_zend_string
struct _zend_string {
zend_refcounted_h gc;
zend_ulong h; /* hash value */
size_t len;
char val[1];
};
struct _zend_array {
zend_refcounted_h gc;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar flags,
zend_uchar nApplyCount,
zend_uchar nIteratorsCount,
zend_uchar consistency)
} v;
uint32_t flags;
} u;
uint32_t nTableMask;
Bucket *arData;
uint32_t nNumUsed;
uint32_t nNumOfElements;
uint32_t nTableSize;
uint32_t nInternalPointer;
zend_long nNextFreeElement;
dtor_func_t pDestructor;
};
注意每个结构体中都有一个名为gc的变量,这个变量其实就是 zend_refcounted_h 引用计数
内存回收的两种情况
-
正常的变量在生命周期完成之后的回收。
这种情况也就是说当zend_value中refCount==0的时候,这时候属于正常的内存回收。
-
垃圾回收
所谓垃圾: 就是指通过循环引用(自己引用自己,目前只在array,object类型中有出现)的形式而导致refcount永远不为0。这种情况下,如果不处理,但是这些内存无法释放,到时内存泄露代码如下:
$a = array(1,2 ); xdebug_debug_zval("a"); $a[] = &$a; xdebug_debug_zval("a"); $b=$a; unset($a); xdebug_debug_zval("a"); xdebug_debug_zval("b");
结果如下
a: (refcount=1, is_ref=0) array (size=2) 0 => (refcount=0, is_ref=0)int 1 1 => (refcount=0, is_ref=0)int 2 a: (refcount=2, is_ref=1) array (size=3) 0 => (refcount=0, is_ref=0)int 1 1 => (refcount=0, is_ref=0)int 2 2 => (refcount=2, is_ref=1) &array< a: (refcount=0, is_ref=0)*uninitialized* b: (refcount=2, is_ref=0) array (size=3) 0 => (refcount=0, is_ref=0)int 1 1 => (refcount=0, is_ref=0)int 2 2 => (refcount=1, is_ref=1) &array<
按照正常逻辑,当unset($a),之后,其对应的zeng_value的refCount=1,is_ref=0,但是实际上它却是等于refCount=2,is_ref=0, 这就导致,就算是unset($b),之后,refCount=1,is_ref=0,这种结果,像上面当refCount=0后正常回收内存。
![图片上传中...]
这就是 垃圾。
当出现垃圾之后,php的zend引擎有对应的垃圾回收机制。
其实这种机制的原理就是 :
(1).将这些垃圾放入buffer中。 (2).当buffer到达一定数量之后,启动对所有垃圾的value自身的refCount-1,并将zend_refcounted中的gc_info变量置为GC_GRAY
typedef struct _zend_refcounted_h { uint32_t refcount; /* reference counter 32-bit */ union { struct { ZEND_ENDIAN_LOHI_3( zend_uchar type, zend_uchar flags, /* used for strings & objects */ uint16_t gc_info) /* 就是这个变量 keeps GC root number (or 0) and color */ } v uint32_t type_info; } u; } zend_refcounted_h;
(3).遍历当前buffer,当refCount=0是则表示当前的这个value的确是个垃圾,则将zend_refcounted中的gc_info变量置为GC_WHITE。然后因为所有的value中都减1,所以再次遍历,将那些减一后refcount !=0 的value+1,然后将zend_refcounted中的gc_info变量置为GC_BLACK (4).最后遍历buffer,将buffer中的value的zend_refcounted中的gc_info为GC_WHITE删除 讲完GC垃圾回收算法的原理,貌似我还没有讲在什么时候会触发将这个**可能**的垃圾放入buffer。。。。。。 ***触发这个机制的时机是每次出现refCount减少时候。***
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。