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中使用接口实现工厂设计模式的代码
Jun 17 PHP
php轻松实现中英文混排字符串截取
May 28 PHP
php实现通用的信用卡验证类
Mar 24 PHP
WordPress中制作导航菜单的PHP核心方法讲解
Dec 11 PHP
php采集神器cURL使用方法详解
Feb 19 PHP
php实现跨域提交form表单的方法【2种方法】
Oct 17 PHP
php获取开始与结束日期之间所有日期的方法
Nov 29 PHP
php json中文编码为null的解决办法
Dec 14 PHP
PHP有序表查找之二分查找(折半查找)算法示例
Feb 09 PHP
php生成静态页面并实现预览功能
Jun 27 PHP
Laravel定时任务的每秒执行代码
Oct 22 PHP
php高性能日志系统 seaslog 的安装与使用方法分析
Feb 29 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 strstr查找字符串中是否包含某些字符的查找函数
2010/06/03 PHP
dhtmlxTree目录树增加右键菜单以及拖拽排序的实现方法
2013/04/26 PHP
php广告加载类用法实例
2014/09/23 PHP
CodeIgniter配置之SESSION用法实例分析
2016/01/19 PHP
PHP排序算法之基数排序(Radix Sort)实例详解
2018/04/21 PHP
checkbox 多选框 联动实现代码
2008/10/22 Javascript
Web跨浏览器进程通信(Web跨域)
2013/04/17 Javascript
JS随机漂浮广告代码具体实例
2013/11/19 Javascript
jQuery实现单击弹出Div层窗口效果(可关闭可拖动)
2015/09/19 Javascript
js前端解决跨域问题的8种方案(最新最全)
2016/11/18 Javascript
Bootstrap基本样式学习笔记之表格(2)
2016/12/07 Javascript
AngularJS模仿Form表单提交的实现代码
2016/12/08 Javascript
快速掌握jQuery插件开发
2017/01/19 Javascript
javascript 面向对象实战思想分享
2017/09/07 Javascript
javascript获取select值的方法完整实例
2019/06/20 Javascript
微信小程序文字显示换行问题
2019/07/28 Javascript
VUE DEMO之模拟登录个人中心页面之间数据传值实例
2019/10/31 Javascript
JS实现导航栏楼层特效
2020/01/01 Javascript
vue 移动端记录页面浏览位置的方法
2020/03/11 Javascript
使用go和python递归删除.ds store文件的方法
2014/01/22 Python
对python使用http、https代理的实例讲解
2018/05/07 Python
Python wxPython库消息对话框MessageDialog用法示例
2018/09/03 Python
pygame游戏之旅 载入小车图片、更新窗口
2018/11/20 Python
详解分布式任务队列Celery使用说明
2018/11/29 Python
Python的Tkinter点击按钮触发事件的例子
2019/07/19 Python
使用Python自动生成HTML的方法示例
2019/08/06 Python
django orm模块中的 is_delete用法
2020/05/20 Python
python+selenium自动化实战携带cookies模拟登陆微博
2021/01/19 Python
Matlab使用Plot函数实现数据动态显示方法总结
2021/02/25 Python
JD Sports荷兰:英国领先的运动时尚零售商
2020/03/13 全球购物
大学毕业感言50字
2014/02/07 职场文书
人事专员工作职责
2014/02/22 职场文书
好书伴我成长演讲稿
2014/05/14 职场文书
Nginx服务器如何设置url链接
2021/03/31 Servers
Redis中缓存穿透/击穿/雪崩问题和解决方法
2021/12/04 Redis
golang连接MySQl使用sqlx库
2022/04/14 Golang