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使之能同时支持GIF和JPEG
Oct 09 PHP
Ajax PHP分页演示
Jan 02 PHP
PHP生成随机用户名和密码的实现代码
Feb 27 PHP
php生成随机数的三种方法
Sep 10 PHP
thinkphp的URL路由规则与配置实例
Nov 26 PHP
php中实现可以返回多个值的函数实例
Mar 21 PHP
PHP简单获取多个checkbox值的方法
Jun 13 PHP
php安装ssh2扩展的方法【Linux平台】
Jul 20 PHP
php实现36进制与10进制转换功能示例
Jan 10 PHP
php workerman定时任务的实现代码
Dec 23 PHP
php生成随机数/生成随机字符串的方法小结【5种方法】
May 27 PHP
phpcmsv9.0任意文件上传漏洞解析
Oct 20 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设计模式 Mediator (中介者模式)
2011/06/26 PHP
PHP自定义错误用法示例
2016/09/28 PHP
PHP获取日期对应星期、一周日期、星期开始与结束日期的方法
2018/06/22 PHP
固定表格行列(expression)在IE下适用
2013/07/25 Javascript
jquery实现动态菜单的实例代码
2013/11/28 Javascript
Jquery如何实现点击时高亮显示代码
2014/01/22 Javascript
jquery获取颜色在ie和ff下的区别示例介绍
2014/03/28 Javascript
优化RequireJS项目的相关技巧总结
2015/07/01 Javascript
Angularjs中使用layDate日期控件示例
2017/01/11 Javascript
JS 实现随机验证码功能
2017/02/15 Javascript
详解ECMAScript6入门--Class对象
2017/04/27 Javascript
Layui table 组件的使用之初始化加载数据、数据刷新表格、传参数
2017/09/11 Javascript
微信小程序scroll-view横向滑动嵌套for循环的示例代码
2018/09/20 Javascript
Puppeteer 爬取动态生成的网页实战
2018/11/14 Javascript
浅谈Javascript中的对象和继承
2019/04/19 Javascript
[02:25]DOTA2英雄基础教程 熊战士
2014/01/03 DOTA
Python2.x版本中cmp()方法的使用教程
2015/05/14 Python
python用match()函数爬数据方法详解
2019/07/23 Python
python opencv将图片转为灰度图的方法示例
2019/07/31 Python
Python字符串大小写转换拼接删除空白
2019/09/19 Python
python使用ctypes库调用DLL动态链接库
2020/10/22 Python
实列教程 一款基于jquery和css3的响应式二级导航菜单
2014/11/13 HTML / CSS
html5视频自动横过来自适应页面且点击播放功能的实现
2020/06/03 HTML / CSS
某公司部分笔试题
2013/11/05 面试题
六查六看剖析材料
2014/02/15 职场文书
党员承诺书格式
2014/05/21 职场文书
求职简历自荐信
2014/06/18 职场文书
离婚协议书范本样本
2014/08/19 职场文书
产品委托授权书范本
2014/09/16 职场文书
领导班子四风对照检查材料
2014/09/23 职场文书
县政府班子个人对照检查材料
2014/10/05 职场文书
2014年酒店工作总结范文
2014/11/17 职场文书
2016秋季幼儿园开学寄语
2015/12/03 职场文书
Vue.js 带下拉选项的输入框(Textbox with Dropdown)组件
2021/04/17 Vue.js
Tomcat用户管理的优化配置详解
2022/03/31 Servers
CDPR谈《巫师》新作用虚幻5原因 称不会为Epic独占
2022/04/06 其他游戏