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缩略图生成程式(需要GD库支持)
Mar 06 PHP
PHPnow安装服务[apache_pn]失败的问题的解决方法
Sep 10 PHP
PHP持久连接mysql_pconnect()函数使用介绍
Feb 05 PHP
php变量作用域的深入解析
Jun 03 PHP
解析curl提交GET,POST,Cookie的简单方法
Jun 29 PHP
php调整gif动画图片尺寸示例代码分享
Dec 05 PHP
php获取英文姓名首字母的方法
Jul 13 PHP
是 WordPress 让 PHP 更流行了 而不是框架
Feb 03 PHP
php生成带logo二维码方法小结
Apr 08 PHP
浅谈mysql_query()函数的返回值问题
Sep 05 PHP
Laravel 前端资源配置教程
Oct 18 PHP
laravel实现图片上传预览,及编辑时可更换图片,并实时变化的例子
Nov 14 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
天使彦史上最神还原,性别曝光的那一刻,百万网友恋爱了
2020/03/02 国漫
PHP 组件化编程技巧
2009/06/06 PHP
探寻PHP脚本不报错的原因
2014/06/12 PHP
PHP实现将MySQL重复ID二维数组重组为三维数组的方法
2016/08/01 PHP
PHPWind9.0手动屏蔽验证码解决后台关闭验证码但是依然显示的问题
2016/08/12 PHP
基于jQueryUI和Corethink实现百度的搜索提示功能
2016/11/09 PHP
php使用curl获取header检测开启GZip压缩的方法
2018/08/15 PHP
科讯商业版中用到的ajax空间与分页函数
2007/09/02 Javascript
JavaScript具有类似Lambda表达式编程能力的代码(改进版)
2010/09/14 Javascript
JS拖动技术 关于setCapture使用
2010/12/09 Javascript
jQuery实现表格行上移下移和置顶的方法
2015/05/22 Javascript
详解JavaScript中循环控制语句的用法
2015/06/03 Javascript
HTML5使用DeviceOrientation实现摇一摇功能
2015/06/05 Javascript
jQuery和CSS仿京东仿淘宝列表导航菜单
2017/01/04 Javascript
JavaScript解析JSON格式数据的方法示例
2017/01/24 Javascript
jQuery is not defined 错误原因与解决方法小结
2017/03/19 Javascript
jQuery插件Echarts实现的渐变色柱状图
2017/03/23 jQuery
详解微信小程序实现WebSocket心跳重连
2018/07/31 Javascript
vue中使用better-scroll实现滑动效果及注意事项
2018/11/15 Javascript
vue如何限制只能输入正负数及小数
2019/07/04 Javascript
Vue自动构建发布脚本的方法示例
2020/07/24 Javascript
原生js实现弹幕效果
2020/11/29 Javascript
video.js添加自定义组件的方法
2020/12/09 Javascript
讲解Python中运算符使用时的优先级
2015/05/14 Python
Python中的os.path路径模块中的操作方法总结
2016/07/07 Python
修改默认的pip版本为对应python2.7的方法
2018/11/06 Python
Python3.5字符串常用操作实例详解
2019/05/01 Python
Python爬虫爬取新闻资讯案例详解
2020/07/14 Python
美国尼曼百货官网:Neiman Marcus
2019/09/05 全球购物
Boolean b = new Boolean(“abcde”); 会编译错误码
2013/11/27 面试题
喜之郎果冻广告词
2014/03/20 职场文书
医院领导班子查摆问题对照检查材料思想汇报
2014/10/08 职场文书
2016年乡镇综治宣传月活动总结
2016/03/16 职场文书
创业计划书之美甲店
2019/09/20 职场文书
《原神》新角色演示“神里绫人:林隐泓洄” 宠妹狂魔
2022/04/03 其他游戏
选购到合适的激光打印机
2022/04/21 数码科技