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程序员面试 切忌急功近利(更需要注重以后的发展)
Sep 01 PHP
php开启安全模式后禁用的函数集合
Jun 26 PHP
linux iconv方法的使用
Oct 01 PHP
PHP面向对象法则
Feb 23 PHP
解析:使用php mongodb扩展时 需要注意的事项
Jun 18 PHP
使用PHP备份MySQL和网站发送到邮箱实例代码
Nov 28 PHP
php实现XSS安全过滤的方法
Jul 29 PHP
使用PHP uniqid函数生成唯一ID
Nov 18 PHP
PHP中header函数的用法及其注意事项详解
Jun 13 PHP
php与python实现的线程池多线程爬虫功能示例
Oct 12 PHP
laravel实现简单用户权限的示例代码
May 28 PHP
php面试实现反射注入的详细方法
Sep 30 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
php代码优化及php相关问题总结
2006/10/09 PHP
php中session_unset与session_destroy的区别分析
2011/06/16 PHP
PHP实现限制域名访问的实现代码(本地验证)
2020/09/13 PHP
js弹出的对话窗口永远保持居中显示
2012/12/15 Javascript
JQuery实现动态表格点击按钮表格增加一行
2014/08/24 Javascript
详谈javascript异步编程
2016/02/21 Javascript
jQuery使用ajax跨域获取数据的简单实例
2016/05/18 Javascript
Bootstrap被封装的弹层
2016/07/20 Javascript
jQuery实现模拟flash头像裁切上传功能示例
2016/12/11 Javascript
JS匹配日期和时间的正则表达式示例
2017/05/12 Javascript
AngularJS全局警告框实现方法示例
2017/05/18 Javascript
javascript简单写的判断电话号码实例
2017/05/24 Javascript
Windows下Node.js安装及环境配置方法
2017/09/18 Javascript
移动端H5页面返回并刷新页面(BFcache)的方法
2018/11/06 Javascript
JavaScript之实现一个简单的Vue示例
2019/01/17 Javascript
Javascript通过控制类名更改样式
2019/05/24 Javascript
JavaScript数值类型知识汇总
2019/11/17 Javascript
nuxt+axios实现打包后动态修改请求地址的方法
2020/04/22 Javascript
基于JavaScript实现大文件上传后端代码实例
2020/08/18 Javascript
python列表去重的二种方法
2014/02/14 Python
python插入排序算法实例分析
2015/07/03 Python
Python实现批量将word转html并将html内容发布至网站的方法
2015/07/14 Python
Python的Flask框架中集成CKeditor富文本编辑器的教程
2016/06/13 Python
对python3中, print横向输出的方法详解
2019/01/28 Python
Python猴子补丁Monkey Patch用法实例解析
2020/03/23 Python
python numpy库np.percentile用法说明
2020/06/08 Python
Python创建临时文件和文件夹
2020/08/05 Python
HTML5中使用json对象的实例代码
2018/09/10 HTML / CSS
Bally澳大利亚官网:瑞士奢侈品牌
2018/11/01 全球购物
Roxy俄罗斯官方网站:冲浪和滑雪板的一切
2020/06/20 全球购物
PHP如何对用户密码进行加密
2014/07/31 面试题
电气个人求职信范文
2014/02/04 职场文书
开展批评与自我批评发言材料
2014/10/17 职场文书
警告通知
2015/04/25 职场文书
Golang 正则匹配效率详解
2021/04/25 Golang
python爬取某网站原图作为壁纸
2021/06/02 Python