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设计模式 Strategy(策略模式)
Jun 26 PHP
在wamp集成环境下升级php版本(实现方法)
Jul 01 PHP
php中用date函数获取当前时间有误的解决办法
Aug 02 PHP
微信公众号点击菜单即可打开并登录微站的实现方法
Nov 14 PHP
详解在PHP的Yii框架中使用行为Behaviors的方法
Mar 18 PHP
使用php完成常见的文件上传功能(推荐)
Jan 13 PHP
Yii2 hasOne(), hasMany() 实现三表关联的方法(两种)
Feb 15 PHP
PHP读取XML格式文件的方法总结
Feb 27 PHP
PHP的curl函数的用法总结
Feb 14 PHP
php扩展开发入门demo示例
Sep 23 PHP
Yii 框架入口脚本示例分析
May 19 PHP
php引用传递
Apr 01 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(视频)Http下载
2006/12/12 PHP
PHP简单实现HTTP和HTTPS跨域共享session解决办法
2015/05/27 PHP
php 调用百度sms来发送短信的实现示例
2018/11/02 PHP
ThinkPHP5 的简单搭建和使用详解
2018/11/15 PHP
ThinkPHP框架实现FTP图片上传功能示例
2019/04/08 PHP
关于Anemometer图形化显示MySQL慢日志的工具搭建及使用的详细介绍
2020/07/13 PHP
onkeyup,onkeydown和onkeypress的区别介绍
2013/10/21 Javascript
Nodejs极简入门教程(二):定时器
2014/10/25 NodeJs
将JavaScript的jQuery库中表单转化为JSON对象的方法
2015/11/17 Javascript
jQuery easyUI datagrid 增加求和统计行的实现代码
2016/06/01 Javascript
微信小程序上滑加载下拉刷新(onscrollLower)分批加载数据(二)
2017/05/11 Javascript
javascript定时器取消定时器及优化方法
2017/07/08 Javascript
XMLHttpRequest对象_Ajax异步请求重点(推荐)
2017/09/28 Javascript
Node.js命令行/批处理中如何更改Linux用户密码浅析
2018/07/22 Javascript
解决Vue 项目打包后favicon无法正常显示的问题
2018/09/01 Javascript
QML实现圆环颜色选择器
2019/09/25 Javascript
Node.JS获取GET,POST数据之queryString模块使用方法详解
2020/02/06 Javascript
python 3.6 tkinter+urllib+json实现火车车次信息查询功能
2017/12/20 Python
Python3 XML 获取雅虎天气的实现方法
2018/02/01 Python
详解python校验SQL脚本命名规则
2019/03/22 Python
Python2与Python3的区别详解
2020/02/09 Python
利用Python实现某OA系统的自动定位功能
2020/05/27 Python
Python GUI库Tkiner使用方法代码示例
2020/11/27 Python
CSS3实现10种Loading效果
2016/07/11 HTML / CSS
SmartBuyGlasses台湾:名牌眼镜,名牌太阳眼镜及隐形眼镜
2017/01/04 全球购物
优秀教师事迹简介
2014/02/02 职场文书
关于环保的建议书400字
2014/03/12 职场文书
英语专业毕业生求职信
2014/05/24 职场文书
十佳少年事迹材料
2014/12/25 职场文书
2015年体育教师个人工作总结
2015/05/12 职场文书
女方家长婚礼答谢词
2015/09/29 职场文书
iPhone13将有八大升级
2021/04/15 数码科技
看看如何用Python绘制小米新版天价logo
2021/04/20 Python
MySQL 慢查询日志深入理解
2021/04/22 MySQL
在JavaScript中如何使用宏详解
2021/05/06 Javascript
分享几个实用的CSS代码块
2022/06/10 HTML / CSS