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执行速率优化技巧小结
Mar 15 PHP
php后退一页表单内容保存实现方法
Jun 17 PHP
深入php之规范编程命名小结
May 15 PHP
PHP利用func_get_args和func_num_args函数实现函数重载实例
Nov 12 PHP
PHP中使用SimpleXML检查XML文件结构实例
Jan 07 PHP
php导入excel文件到mysql数据库的方法
Jan 14 PHP
php去除字符串中空字符的常用方法小结
Mar 17 PHP
ThinkPHP表单令牌错误的相关解决方法分析
May 20 PHP
php基于单例模式封装mysql类完整实例
Oct 18 PHP
在IIS下安装PHP扩展的方法(超简单)
Apr 10 PHP
PHP策略模式定义与用法示例
Jul 27 PHP
PHP封装mysqli基于面向对象的mysql数据库操作类与用法示例
Feb 25 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实现用户认证及管理完全源码
2007/03/11 PHP
php 求质素(素数) 的实现代码
2011/04/12 PHP
那些年我们错过的魔术方法(Magic Methods)
2014/01/14 PHP
Zend Framework页面缓存实例
2014/06/25 PHP
php处理复杂xml数据示例
2016/07/11 PHP
PHP仿qq空间或朋友圈发布动态、评论动态、回复评论、删除动态或评论的功能(上)
2017/05/26 PHP
php实现网页上一页下一页翻页过程详解
2019/06/28 PHP
jQuery dialog 异步调用ashx,webservice数据的代码
2010/08/03 Javascript
自定义ExtJS控件之下拉树和下拉表格附源码
2013/10/15 Javascript
JS小功能(checkbox实现全选和全取消)实例代码
2013/11/28 Javascript
动态的绑定事件addEventListener方法的使用
2014/01/24 Javascript
JavaScript显示当前文档最后修改日期的方法
2015/03/19 Javascript
JQuery判断checkbox是否选中及其它复选框操作方法合集
2015/06/01 Javascript
设计模式中的facade外观模式在JavaScript开发中的运用
2016/05/18 Javascript
JavaScript SHA1加密算法实现详细代码
2016/10/06 Javascript
Vue.js中轻松解决v-for执行出错的三个方案
2017/06/09 Javascript
Node.js console控制台简单用法分析
2019/01/04 Javascript
为vue项目自动设置请求状态的配置方法
2019/06/09 Javascript
JavaScript实现简单验证码
2020/08/24 Javascript
Windows下使Python2.x版本的解释器与3.x共存的方法
2015/10/25 Python
python Xpath语法的使用
2020/11/26 Python
CSS3与动画有关的属性transition、animation、transform对比(史上最全版)
2017/08/18 HTML / CSS
HTML5+CSS3实现拖放(Drag and Drop)示例
2014/07/07 HTML / CSS
Lenox官网:精美的瓷器&独特的礼品
2017/02/12 全球购物
Sneaker Studio罗马尼亚网站:购买运动鞋
2018/11/04 全球购物
理肤泉英国官网:La Roche-Posay英国
2019/01/14 全球购物
用Python写一个for循环的例子
2016/07/19 面试题
彩色的翅膀教学反思
2014/04/25 职场文书
党员群众路线对照检查材料
2014/08/31 职场文书
忠诚教育心得体会
2014/09/03 职场文书
2015年毕业生自我鉴定模板
2014/09/19 职场文书
开展党的群众路线教育实践活动总结报告
2014/10/31 职场文书
实习工作表现评语
2014/12/31 职场文书
综合素质评价个性与发展自我评价
2015/03/06 职场文书
消费者投诉书范文
2015/07/02 职场文书
大学学生会主席竞选稿
2015/11/19 职场文书