PHP垃圾回收机制

PHP垃圾回收机制

往上面讲述PHP垃圾回收机制的文章很多,官网的介绍,还有下面一些

等等。

新的垃圾回收机制,是为了防止引用计数中变量的循环引用引起的内存泄露,如果没有主动unset变量,首先变量的赋值操作会进行其引用数-1是否为0的判断,如果不为0,加入垃圾回收的机制的代码。如果主动unset 的话,跟踪代码,会调用_zval_ptr_dtor函数,进而调用GC_ZVAL_CHECK_POSSIBLE_ROOT执行垃圾回收机制。

代码才是硬道理:

static inline zval* zend_assign_to_variable(zval **variable_ptr_ptr, zval *value, int is_tmp_var TSRMLS_DC) 
{ 
    zval *variable_ptr = *variable_ptr_ptr; 
    zval garbage; 
  
    if (variable_ptr == EG(error_zval_ptr)) { 
        if (is_tmp_var) { 
            zval_dtor(value); 
        } 
        return EG(uninitialized_zval_ptr); 
    } 
  
    if (Z_TYPE_P(variable_ptr) == IS_OBJECT && Z_OBJ_HANDLER_P(variable_ptr, set)) { 
        Z_OBJ_HANDLER_P(variable_ptr, set)(variable_ptr_ptr, value TSRMLS_CC); 
        return variable_ptr; 
    } 
  //变量赋值的各种情况 
    if (PZVAL_IS_REF(variable_ptr)) { 
        if (variable_ptr!=value) { 
            zend_uint refcount = Z_REFCOUNT_P(variable_ptr); 
  
            garbage = *variable_ptr; 
            *variable_ptr = *value; 
            Z_SET_REFCOUNT_P(variable_ptr, refcount); 
            Z_SET_ISREF_P(variable_ptr); 
            if (!is_tmp_var) { 
                zendi_zval_copy_ctor(*variable_ptr); 
            } 
            zendi_zval_dtor(garbage); 
            return variable_ptr; 
        } 
    } else { 
        if (Z_DELREF_P(variable_ptr)==0) {//判断变量引用数-1后是否为0 
            if (!is_tmp_var) { 
                if (variable_ptr==value) {//左右值是否相等 
                    Z_ADDREF_P(variable_ptr); 
                } else if (PZVAL_IS_REF(value)) { 
                    garbage = *variable_ptr; 
                    *variable_ptr = *value; 
                    INIT_PZVAL(variable_ptr); 
                    zval_copy_ctor(variable_ptr); 
                    zendi_zval_dtor(garbage); 
                    return variable_ptr; 
                } else { 
                    Z_ADDREF_P(value); 
                    *variable_ptr_ptr = value; 
                    if (variable_ptr != &EG(uninitialized_zval)) { 
                        GC_REMOVE_ZVAL_FROM_BUFFER(variable_ptr); 
                        zval_dtor(variable_ptr); 
                        efree(variable_ptr); 
                    } 
                    return value; 
                } 
            } else { 
                garbage = *variable_ptr; 
                *variable_ptr = *value; 
                INIT_PZVAL(variable_ptr); 
                zendi_zval_dtor(garbage); 
                return variable_ptr; 
            } 
        } else { /* we need to split */ 
            GC_ZVAL_CHECK_POSSIBLE_ROOT(*variable_ptr_ptr); //若不为零加入垃圾回收机制 (不知道增加if (variable_ptr==value) 条件会不会更好?循环引用嘛,直接判断循环又没成立,给自己留个问题) 
            if (!is_tmp_var) { 
                if (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) { 
                    ALLOC_ZVAL(variable_ptr); 
                    *variable_ptr_ptr = variable_ptr; 
                    *variable_ptr = *value; 
                    Z_SET_REFCOUNT_P(variable_ptr, 1); 
                    zval_copy_ctor(variable_ptr); 
                } else { 
                    *variable_ptr_ptr = value; 
                    Z_ADDREF_P(value); 
                } 
            } else { 
                ALLOC_ZVAL(*variable_ptr_ptr); 
                Z_SET_REFCOUNT_P(value, 1); 
                **variable_ptr_ptr = *value; 
            } 
        } 
        Z_UNSET_ISREF_PP(variable_ptr_ptr); 
    } 
  
    return *variable_ptr_ptr; 
}

上面是变量赋值的时候,会调用的函数。

unset 的时候,会将opcode 的类型改变,下面是unset 的代码:

void zend_do_unset(const znode *variable TSRMLS_DC) /* {{{ */
{ 
    zend_op *last_op; 
  
    zend_check_writable_variable(variable); 
  
    if (variable->op_type == IS_CV) { 
        zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); 
        opline->opcode = ZEND_UNSET_VAR; 
        opline->op1 = *variable; 
        SET_UNUSED(opline->op2); 
        opline->op2.u.EA.type = ZEND_FETCH_LOCAL; 
        SET_UNUSED(opline->result); 
        opline->extended_value = ZEND_QUICK_SET; 
    } else { 
        last_op = &CG(active_op_array)->opcodes[get_next_op_number(CG(active_op_array))-1]; 
  
        switch (last_op->opcode) { 
            case ZEND_FETCH_UNSET: 
                last_op->opcode = ZEND_UNSET_VAR; 
                break; 
            case ZEND_FETCH_DIM_UNSET: 
                last_op->opcode = ZEND_UNSET_DIM; 
                break; 
            case ZEND_FETCH_OBJ_UNSET: 
                last_op->opcode = ZEND_UNSET_OBJ; 
                break; 
  
        } 
    }

在执行函数中,临时变量会调用zval_dtor(直接销毁),一般的变量会调用zval_ptr_dtor。

#define zval_ptr_dtor(zval_ptr) _zval_ptr_dtor((zval_ptr) ZEND_FILE_LINE_CC) 
  
//在_zval_ptr_dtor函数中增加垃圾回收机制 
  
ZEND_API void _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC) /* {{{ */
{ 
#if DEBUG_ZEND>=2 
    printf("Reducing refcount for %x (%x): %d->%d\n", *zval_ptr, zval_ptr, Z_REFCOUNT_PP(zval_ptr), Z_REFCOUNT_PP(zval_ptr) - 1); 
#endif
    Z_DELREF_PP(zval_ptr); 
    if (Z_REFCOUNT_PP(zval_ptr) == 0) { 
        TSRMLS_FETCH(); 
  
        if (*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_PP(zval_ptr) == 1) { 
            Z_UNSET_ISREF_PP(zval_ptr); 
        } 
  
        GC_ZVAL_CHECK_POSSIBLE_ROOT(*zval_ptr);//here!! 
    } 
}

至于垃圾回收机制的算法,一些数据机构,其他的文章写的已经相当详细,不为自己做记录了。