在某些情况下,为什么unset只能销毁变量,却无法清除内存中的值以及真正释放内存

在PHP中,unset函数是用来清除变量,但在某些情况unset的行为会因为要销毁的变量的类型而有所不同。 
我们都知道PHP是动态的弱类型语言,而且PHP的写机制会使用内存处理的引用计数的复本,PHP中变量主要
有符号表中的条目和实际的变量容器,变量容器如下定义:其中refcount__gc和is_ref__gc分别表示此变
量容器的引用次数和是否被引用。
typedef struct _zval_struct zval;
typedef union _zvalue_value {    long lval;                 /* long value */
    double dval;               /* double value */
    struct {                   /* string type */
        char *val;
        int len;
    } str;
    HashTable *ht;             /* hash table value */
    zend_object_value obj;
} zvalue_value;
 
struct _zval_struct {
    /* Variable information */
    zvalue_value value;        /* value */
    zend_uint refcount__gc;
    zend_uchar type;           /* active type */
    zend_uchar is_ref__gc;
};
下面我们使用Xdebug查看当某个变量被赋值通过引用时,refcount__gc与is_ref__gc在变量容器中的情况。
测试代码<?php $a = 'me';
 xdebug_debug_zval('a');
$b = &$a;
xdebug_debug_zval('a');
xdebug_debug_zval('a');
$b = 'too'; 
var_dump($a);
unset($a);
xdebug_debug_zval('b');?> 程序输出的情况如下:
a: (refcount=1, is_ref=0)='me'
a: (refcount=2, is_ref=1)='me'
b: (refcount=2, is_ref=1)='me'
string(3) "too"
b: (refcount=1, is_ref=0)='too'
从上面的输出中,可以知道变量a,b共用同一个变量容器,当调用unset  销毁a时,只是改变变量容器的引用计数器,并没有
实际清除变量容器,而是取消相应的符号表条目与该变量映射并清除。
下面再看一个函数内部调用unset消费全局变量的情景,测试代码如下:
<?php
function test()
{
  global $a;
  xdebug_debug_zval('a');
  unset($a);
  xdebug_debug_zval('a');
}
$a = 'me';
xdebug_debug_zval('a');
test();
var_dump($a);
xdebug_debug_zval('a');
?>
运行输出的结果如下:
a: (refcount=1, is_ref=0)='me'
a: (refcount=2, is_ref=1)='me'
a: (refcount=1, is_ref=0)='me'
string(2) "me"
a: (refcount=1, is_ref=0)='me'
由上面的结果可以知道,当在函数内部引用全局变量时,会使全局变量对应的变量容器的引用计数器加一同时说明此变量被引用
过,类似与通过引用传递的变量情景,当在函数内部使用unset  销毁全局变量仅是将相应的引用计数器值以及是否引用做改变。
有时我也在想,当在函数内部调用全部变量会不会临时在符号表中添加新的一条目,符号标志也是' a',  只不过作用域是函数
内部,具体的情况还没来得及细看,有机会继续深入。
   从上可以知道 unset  在撤销变量时,会对变量对应的变量容器操作, 同时撤销掉相应的符号表中的条目,至于内存是否
被销毁,需相应的计数器值为零,我将第一个例子的代码后面又添加三行代码,
unset($b);
echo 'over';
xdebug_debug_zval('b');
当程序运行后结果如下:
a: (refcount=1, is_ref=0)='me'
a: (refcount=2, is_ref=1)='me'
b: (refcount=2, is_ref=1)='me'
string(3) "too"
b: (refcount=1, is_ref=0)='too'
over
     由结果可以知道,当对变量b再使用unse时,就会使变量容器被销毁掉,既内存的值被清除掉,相应的内存块也被释放。

你可能感兴趣的:(在某些情况下,为什么unset只能销毁变量,却无法清除内存中的值以及真正释放内存)