PHP线程的内存回收问题


Posted in PHP onJuly 08, 2016

当一个PHP线程结束时,当前占用的所有内存空间都会被销毁。那么如果这个线程不结束,怎么回收内存呢?

refcount:引用技术器,可以理解为指向该个容器的指针个数吧。

is_ref:是否被引用(只可能是0或者1)

赋值的流程:

<?php
$a = 'aa';
xdebug_debug_zval(a); //(refcount=1, is_ref=0),string 'aa' (length=6)
$b = $a; 
//以下的两个其实是一个变量容器
xdebug_debug_zval(a); //(refcount=2, is_ref=0),string 'aa' (length=6)
xdebug_debug_zval(b); //(refcount=2, is_ref=0),string 'aa' (length=6)
unset($b); //对变量容器 refcount 减1
xdebug_debug_zval(a); //(refcount=1, is_ref=0),string 'aa' (length=6)
xdebug_debug_zval(b); //b: no such symbol b变量被销毁,指向被断掉,如果对应容器的引用技术为零,那么该块儿内存被回收
$b = $a;
$b = 'bb';
xdebug_debug_zval(a); //(refcount=1, is_ref=0),string 'aa' (length=6)
xdebug_debug_zval(b); //(refcount=1, is_ref=0),string 'aa' (length=6) 重新申请一个变量容器存储b,a的变量容器引用减1

引用的流程:

<?php
$a = 'aa';
xdebug_debug_zval('a'); //(refcount=1, is_ref=0),string 'aa' (length=2)
$b = & $a;
//变量容器的引用技术加1,引用标记置为1
xdebug_debug_zval('a'); //(refcount=2, is_ref=1),string 'aa' (length=2)
xdebug_debug_zval('b'); //(refcount=2, is_ref=1),string 'aa' (length=2)
$b = '123'; 
//php会发现,该容器变量是引用(is_ref),所以容器变量不用像赋值那样再申请一个
xdebug_debug_zval('a'); //(refcount=2, is_ref=1),string '123' (length=2)
xdebug_debug_zval('b'); //(refcount=2, is_ref=1),string '123' (length=2)
unset($b);
//变量容器应用计数减1,引用为零
xdebug_debug_zval('a'); //(refcount=1, is_ref=0),string '123' (length=2)
xdebug_debug_zval('b'); // b: no such symbol

那如果多次引用,unset掉一个,is_ref是否会被置为零,那样bug不就出现了么?变量容器还是引用啊。那么我们来看看:

<?php
$a = 'aa';
$b = &$a;
$c = &$a;
//可以看到引用refCount是3,is_ref永远是1
xdebug_debug_zval('a'); //(refcount=3, is_ref=1),string 'aa' (length=2)
xdebug_debug_zval('b'); //(refcount=3, is_ref=1),string 'aa' (length=2)
xdebug_debug_zval('c'); //(refcount=3, is_ref=1),string 'aa' (length=2)

unset($b);
//我们期待的bug没有出现,只是refcount减1,is_ref还是1
xdebug_debug_zval('a'); //(refcount=2, is_ref=1),string 'aa' (length=2)
xdebug_debug_zval('b'); // b: no such symbol
xdebug_debug_zval('c'); //(refcount=2, is_ref=1),string 'aa' (length=2)
//那php它怎么知道这个容器还有引用,毕竟is_ref仍然是1,不能计数,那么现在refcount就起作用了,是它告诉php,该变量有几个引用,但问题又来了,如果我干点坏事,在引用的时候,又赋值,它会不会有bug
$e = $a;
//我们看到期望的bug还是没出现,这时候再赋值,就不像直接赋值那么简单refcount加1了,而是申请了一个新的变量容器
xdebug_debug_zval('a'); //(refcount=2, is_ref=1),string 'aa' (length=2)
xdebug_debug_zval('e'); //(refcount=1, is_ref=0),string 'aa' (length=2)

unset和赋值null都能回收变量么?很多人都错认为,这两个都能回收变量空间,其实错了,null只是把变量占用的空间变小了,从回收上来说,该容器依然存在。

<?php
$a = 'aa';
$b = $a;
$b = null;
//又申请了一个变量容器
xdebug_debug_zval('a'); //(refcount=1, is_ref=0),string 'aa' (length=2)
xdebug_debug_zval('b'); //(refcount=1, is_ref=0),null 变量空间并没被回收
unset($b);
//这时候才释放了b变量容器的空间
xdebug_debug_zval('a'); //(refcount=1, is_ref=0),string 'aa' (length=2)
xdebug_debug_zval('b'); //b: no such symbol

总结

1. 垃圾回收的时机

PHP中,引用计数为0,则内存立刻释放。也就是说,不存在环状引用的变量,离开变量的作用域,内存被立刻释放。环状引用检测则是在满足一定条件下触发,所以在上面的例子中,会看到使用的内存有大幅度的波动。也可以通过 gc_collect_cycles 函数来主动进行环状引用检测。

2. &符号的影响

显式引用一个变量,会增加该内存的引用计数:

$a = "something";
$b = &$a;
此时unset($a), 但是仍有$b指向该内存区域的引用,内存不会释放。

3. unset函数的影响

unset只是断开一个变量到一块内存区域的连接,同时将该内存区域的引用计数-1;在上面的例子中,循环体内部,$a=new A(); unset($a);并不会将$a的引用计数减到零;

4. = null 操作的影响;

$a = null 是直接将$a 指向的数据结构置空,同时将其引用计数归0。

5. 脚本执行结束的影响

脚本执行结束,该脚本中使用的所有内存都会被释放,不论是否有引用环。

PHP 相关文章推荐
php中比较简单的导入phpmyadmin生成的sql文件的方法
Jun 28 PHP
PHP源代码数组统计count分析
Aug 02 PHP
php开启与关闭错误提示适用于没有修改php.ini的权限
Oct 16 PHP
WordPress中is_singular()函数简介
Feb 05 PHP
ThinkPHP开发框架函数详解:C方法
Aug 14 PHP
PHP中Http协议post请求参数
Nov 02 PHP
PHP性能分析工具xhprof的安装使用与注意事项
Dec 19 PHP
支持汉转拼和拼音分词的PHP中文工具类ChineseUtil
Feb 23 PHP
php-fpm添加service服务的例子
Apr 27 PHP
Laravel框架之解决前端显示图片问题
Oct 24 PHP
php上传后台无法收到数据解决方法
Oct 28 PHP
PHP接口类(interface)的定义、特点和应用示例
May 18 PHP
php实现xml与json之间的相互转换功能实例
Jul 07 #PHP
PHP登录验证码的实现与使用方法
Jul 07 #PHP
PHP常见的6个错误提示及解决方法
Jul 07 #PHP
php生成mysql的数据字典
Jul 07 #PHP
php自定义函数实现JS的escape的方法示例
Jul 07 #PHP
PHP使用mysql与mysqli连接Mysql数据库用法示例
Jul 07 #PHP
PHP使用自定义方法实现数组合并示例
Jul 07 #PHP
You might like
15种PHP Encoder的比较
2007/04/17 PHP
shopex主机报错误请求解决方案(No such file or directory)
2011/12/27 PHP
php下载文件,添加响应头的简单实例
2016/09/22 PHP
基于swoole实现多人聊天室
2018/06/14 PHP
详解no input file specified 三种解决方法
2019/11/29 PHP
JavaScript 联动的无限级封装类,数据采用非Ajax方式,随意添加联动
2010/06/29 Javascript
表单切换,用回车键替换Tab健(不支持IE)
2011/07/20 Javascript
也说JavaScript中String类的replace函数
2011/09/22 Javascript
Extjs 继承Ext.data.Store不起作用原因分析及解决
2013/04/15 Javascript
js实现收缩菜单效果实例代码
2013/10/30 Javascript
textarea 控制输入字符字节数(示例代码)
2013/12/27 Javascript
js输出阴历、阳历、年份、月份、周示例代码
2014/01/29 Javascript
将HTML格式的String转化为HTMLElement的实现方法
2014/08/07 Javascript
angularjs 处理多个异步请求方法汇总
2015/01/06 Javascript
React Native 集成jpush-react-native的示例代码
2017/08/16 Javascript
vue购物车插件编写代码
2017/11/27 Javascript
微信小程序实现树莓派(raspberry pi)小车控制
2020/02/12 Javascript
JS数组转字符串实现方法解析
2020/09/04 Javascript
[51:06]2018DOTA2亚洲邀请赛3月29日 小组赛A组 KG VS Liquid
2018/03/30 DOTA
Python有序字典简单实现方法示例
2017/09/28 Python
pandas 数据实现行间计算的方法
2018/06/08 Python
python xlsxwriter创建excel图表的方法
2018/06/11 Python
python单线程文件传输的实例(C/S)
2019/02/13 Python
维氏瑞士军刀英国网站:Victorinox英国
2019/07/04 全球购物
Yves Rocher捷克官方网站:植物化妆品的创造者
2019/07/31 全球购物
如何通过 CSS 写出火焰效果
2021/03/24 HTML / CSS
预备党员党校学习自我评价分享
2013/11/12 职场文书
早餐连锁店计划书
2014/01/08 职场文书
一月红领巾广播稿
2014/02/11 职场文书
小学教师寄语大全
2014/04/03 职场文书
慰问敬老院活动总结
2014/04/26 职场文书
2014坚持党风廉政建设思想汇报
2014/09/18 职场文书
招商引资工作汇报材料
2014/10/28 职场文书
启迪人心的励志语录:脾气永远不要大于本事
2020/01/02 职场文书
如何理解Vue前后端数据交互与显示
2021/05/10 Vue.js
React如何使用axios请求数据并把数据渲染到组件
2022/08/05 Javascript