扫码关注官方订阅号
下列代码执行结果仍为1
<?php $a = 1; $b =& $a; unset($a); echo $b; ?>
请问各位大牛,怎么做到真正的清空这段内存?
拥有18年软件开发和IT教学经验。曾任多家上市公司技术总监、架构师、项目经理、高级软件工程师等职务。 网络人气名人讲师,...
要搞懂这个问题,首先要熟悉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() 之前一样的值。
微信扫码关注PHP中文网服务号
QQ扫码加入技术交流群
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
PHP学习
技术支持
返回顶部
要搞懂这个问题,首先要熟悉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销毁掉。
output:
对常量来说,到底是引用还是内存值,我们通过下面的方式来验证:
输出如下:
如果是引用存储的话,那么第二个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...