PHP垃圾回收机制
往上面讲述PHP垃圾回收机制的文章很多,官网的介绍,还有下面一些。 新的垃圾回收机制,是为了防止引用计数中变量的循环引用引起的内存泄露。如果没有主动unset变量,首先变量的赋值操作会进行其引用数-1是否为0的判断,如果不为0,加入垃圾回收的机制的代码。如果主动unset 的话,跟踪代码,会调用_zval_ptr_dtor函数,进而调用GC_ZVAL_CHECK_POSSIBLE_ROOT执行垃圾回收机制。 代码才是硬道理: ```php 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, value, 0 TSRMLS_CC); return variable_ptr; } if (is_tmp_var) { zval_ptr_dtor(&value); } if (Z_REFCOUNT_PP(variable_ptr_ptr) > 1) { Z_DELREF_PP(variable_ptr_ptr); } else { zval_ptr_dtor(variable_ptr_ptr); } *variable_ptr_ptr = value; return value; } 上面是变量赋值的时候,会调用的函数。
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!! } } 至于垃圾回收机制的算法,一些数据机构,其他的文章写的已经相当详细,不为自己做记录了。