• PHP垃圾回收机制


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

      http://php-internal.com/book/?p=chapt06/06-04-01-new-garbage-collection

      http://blog.csdn.net/phpkernel/article/details/5734743等等。

      我在这边做一下心得的笔记,新的垃圾回收机制,是为了防止引用计数中变量的循环引用引起的内存泄露,如果没有主动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!!
    	}
    }
    

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

  • 相关阅读:
    在Ubuntu18.04.2LTS上安装搜狗输入法
    生活点滴:java基础知识细化
    乘风破浪:LeetCode真题_041_First Missing Positive
    乘风破浪:LeetCode真题_040_Combination Sum II
    乘风破浪:LeetCode真题_039_Combination Sum
    乘风破浪:LeetCode真题_038_Count and Say
    乘风破浪:LeetCode真题_037_Sudoku Solver
    乘风破浪:LeetCode真题_036_Valid Sudoku
    乘风破浪:LeetCode真题_035_Search Insert Position
    乘风破浪:LeetCode真题_034_Find First and Last Position of Element in Sorted Array
  • 原文地址:https://www.cnblogs.com/zoro/p/2152378.html
Copyright © 2020-2023  润新知