php中unset变量之后只是断开了变量名和变量内容之间的绑定,变量内容并没有被销毁吗?

雁丘过客
  • 66

下列代码执行结果仍为1

<?php
    $a = 1;
    $b =& $a;
    unset($a);
    echo $b;
?>

请问各位大牛,怎么做到真正的清空这段内存?

回复
阅读 2.9k
4 个回答

要搞懂这个问题,首先要熟悉PHP底层实现中的两个概念:变量类型和符号表。

在PHP的底层实现,也就是zval中,对PHP的变量类型分为了两种实现形式:对于常规类型,数据的值是直接存储在zval中的;对于对象、数组这类复杂的类型,数据的值是剥离zval,存储在内存的其他地方的,而zval中只存储了那块存储数据的内存地址。

符号表是PHP程序中变量名称和zval值对应关系的存储场所,在PHP底层实现中,是使用HashTable实现的。

在我们unset变量时,实际上PHP只是从符号表里标记清除了这个变量的zval。但由于HashTable是直接存储zval而非引用存储的,所以符号表中所占用的zval内存实际上没有真正释放。当然,在某些对符号表的操作中,可能会触发符号表的自我伸缩,这时候才会真正的对内存进行申请和释放。
另外一方面,如果我们unset的是PHP的复杂类型,除了符号表中的zval外,数据的内容实际上存储在另外一块内存中。对于这种引用类型,PHP都会带有一个refcount引用计数,当zval被清除或者其他操作减少了对象的引用时,变量的引用计数会减一。而如果引用计数为零,则表示变量已经不在PHP程序中被使用了。而对于这类变量,PHP的垃圾回收机制会进行回收并释放那一块的内存。

在内存堆栈中,a,b只是指向1的引用。只有把指向1的所有引用都销毁掉,才能真正的将1销毁掉。

<?php
    $a = 1;
    $b =& $a;
    unset($a, $b);
    echo $b;
?>

output:

PHP Notice:  Undefined variable: b in /path/to/file/a.php on line 5

Notice: Undefined variable: b in /path/to/file/a.php on line 5

对常量来说,到底是引用还是内存值,我们通过下面的方式来验证:

<?php
    $a = 1;
    $b =$a;
    var_dump($a,$b);
    $a = 2;
    var_dump($a,$b);

?>

输出如下:

int(1)
int(1)
int(2)
int(1)

如果是引用存储的话,那么第二个var_dump输出应该是2,2.

你也可以用debug_zval_dump 这个函数来验证。

参考:http://php.net/manual/zh/function.debug-zval-dump.php

这属于底层概念,现代语言大多数都带有垃圾回收机制的。
大多数垃圾回收机制是采用引用计数的来实现的,当你把$a地址引用到$b的时候,这一块内存的引用就会+1。当你在unset($a)的时候代码的引用计数-1。但是并不为0,这代表这块内存还是有别的地方在使用着呢,禁止被回收!

所以在当你没有符号引用$a这段内存的时候,被GC扫描到,这段内存就会被回收了。

不能用c/c++的思想去理解这样的语言,因为很多东西你是不可控的,比如内存管理。


分隔符,如果有新手对楼主的结果有异议的话,可以在线运行一下试试
https://www.bytelang.com/o/s/...

我就贴一下手册 http://php.net/manual/zh/func...

如果在函数中 unset() 一个通过引用传递的变量,则只是局部变量被销毁,而在调用环境中的变量将保持调用 unset() 之前一样的值
如果在函数中 unset() 一个全局变量,则只是局部变量被销毁,而在调用环境中的变量将保持调用 unset() 之前一样的值。
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏