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多图上传小程序代码
Jul 17 PHP
PHP实现AES256加密算法实例
Sep 22 PHP
php中get_cfg_var()和ini_get()的用法及区别
Mar 04 PHP
ThinkPHP在Cli模式下使用模板引擎的方法
Sep 25 PHP
在PHP中使用FastCGI解析漏洞及修复方案
Nov 10 PHP
Zend Framework教程之Zend_Layout布局助手详解
Mar 04 PHP
ThinkPHP使用Smarty第三方插件方法小结
Mar 19 PHP
PHP的Yii框架中View视图的使用进阶
Mar 29 PHP
php使用curl并发减少后端访问时间的方法分析
May 12 PHP
PHP购物车类Cart.class.php定义与用法示例
Jul 20 PHP
PHP基于imagick扩展实现合成图片的两种方法【附imagick扩展下载】
Nov 14 PHP
浅谈laravel中的关联查询with的问题
Oct 10 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 Memcached应用实现代码
2010/02/08 PHP
PHP判断数据库中的记录是否存在的方法
2014/11/14 PHP
破除网页鼠标右键被禁用的绝招大全
2006/12/27 Javascript
Prototype使用指南之hash.js
2007/01/10 Javascript
jQuery选择头像并实时显示的代码
2010/06/27 Javascript
jquery-tips悬浮提示插件分享
2015/07/31 Javascript
jquery实现页面虚拟键盘特效
2015/08/08 Javascript
信息页文内画中画广告js实现代码(文中加载广告方式)
2016/01/03 Javascript
用NODE.JS中的流编写工具是要注意的事项
2016/03/01 Javascript
Javascript随机标签云代码实例
2016/06/21 Javascript
Bootstrap的modal拖动效果
2016/12/25 Javascript
BOM之navigator对象和用户代理检测
2017/02/10 Javascript
JavaScript基本类型值-Undefined、Null、Boolean
2017/02/23 Javascript
浅谈react前后端同构渲染
2017/09/20 Javascript
Angular4的输入属性与输出属性实例详解
2017/11/29 Javascript
vuex 项目结构目录及一些简单配置介绍
2018/04/08 Javascript
使用ECharts实现状态区间图
2018/10/25 Javascript
webpack开发环境和生产环境的深入理解
2018/11/08 Javascript
vue前后分离调起微信支付
2019/07/29 Javascript
json字符串对象转换代码实例
2019/09/28 Javascript
js 实现碰撞检测的示例
2020/10/28 Javascript
antd table按表格里的日期去排序操作
2020/11/17 Javascript
使用Python的Twisted框架编写简单的网络客户端
2015/04/16 Python
深入学习python的yield和generator
2016/03/10 Python
Python爬虫之正则表达式基本用法实例分析
2018/08/08 Python
详解python百行有效代码实现汉诺塔小游戏(简约版)
2020/10/30 Python
Python爬虫新手入门之初学lxml库
2020/12/20 Python
css3.0 图形构成实例练习二
2013/03/19 HTML / CSS
CSS3 实用技巧:实现黑白图像效果示例代码
2013/07/11 HTML / CSS
英国户外装备商店:Ultimate Outdoors
2019/05/07 全球购物
Geekbuying波兰:购买中国电子产品
2019/10/20 全球购物
怎么写好自荐书
2014/03/02 职场文书
小学生倡议书范文
2014/05/13 职场文书
卖车协议书范例
2014/09/16 职场文书
2014年学校团委工作总结
2014/12/20 职场文书
检讨书之工作不认真
2019/08/14 职场文书