2

自嘲)。。。。。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减少时候。***
    
       
    
    
    
    
    

frankie
79 声望7 粉丝