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默认安装产生系统漏洞
Oct 09 PHP
Drupal 添加模块出现莫名其妙的错误的解决方法(往往出现在模块较多时)
Apr 18 PHP
php设计模式 Proxy (代理模式)
Jun 26 PHP
PHP学习笔记之数组篇
Jun 28 PHP
PHP异步调用socket实现代码
Jan 12 PHP
异步加载技术实现当滚动条到最底部的瀑布流效果
Sep 16 PHP
php中使用session防止用户非法登录后台的方法
Jan 27 PHP
PHP使用正则表达式获取微博中的话题和对象名
Jul 18 PHP
PHP Oauth授权和本地加密实现方法
Aug 12 PHP
php is_executable判断给定文件名是否可执行实例
Sep 26 PHP
Yii针对添加行的增删改查操作示例
Oct 18 PHP
tp框架(thinkPHP)实现三次登陆密码错误之后锁定账号功能示例
May 24 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获取网络上文件
2006/10/09 PHP
C# Assembly类访问程序集信息
2009/06/13 PHP
PHP 开发环境配置(Zend Server安装)
2010/04/28 PHP
PHP 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数
2012/04/09 PHP
PHP入门教程之自定义函数用法详解(创建,调用,变量,参数,返回值等)
2016/09/11 PHP
PHP之认识(二)关于Traits的用法详解
2019/04/11 PHP
laravel通用化的CURD的实现
2019/12/13 PHP
学习ExtJS TextField常用方法
2009/10/07 Javascript
AJAX使用了UpdatePanel后无法使用alert弹出脚本
2010/04/02 Javascript
JavaScript单元测试ABC
2012/04/12 Javascript
jquery实现div阴影效果示例代码
2013/09/16 Javascript
Javascript写入txt和读取txt文件示例
2014/02/12 Javascript
js实现仿MSN带关闭功能的右下角弹窗代码
2015/09/04 Javascript
用NODE.JS中的流编写工具是要注意的事项
2016/03/01 Javascript
详解Backbone.js框架中的模型Model与其集合collection
2016/05/05 Javascript
详解React Native网络请求fetch简单封装
2017/08/10 Javascript
webpack vue 项目打包生成的文件,资源文件报404问题的修复方法(总结篇)
2018/01/09 Javascript
Vue实现点击时间获取时间段查询功能
2020/08/21 Javascript
关于layui toolbar和template的结合使用方法
2019/09/19 Javascript
Layui实现数据表格默认全部显示(不要分页)
2019/10/26 Javascript
react-native聊天室|RN版聊天App仿微信实例|RN仿微信界面
2019/11/12 Javascript
JS 创建对象的模式实例小结
2020/04/28 Javascript
vue 单页应用和多页应用的优劣
2020/10/22 Javascript
python中文分词,使用结巴分词对python进行分词(实例讲解)
2017/11/14 Python
python按综合、销量排序抓取100页的淘宝商品列表信息
2018/02/24 Python
Python迭代器与生成器基本用法分析
2018/07/26 Python
使用Python快速制作可视化报表的方法
2019/02/03 Python
【python】matplotlib动态显示详解
2019/04/11 Python
Pandas之排序函数sort_values()的实现
2019/07/09 Python
用纯css3实现的图片放大镜特效效果非常不错
2014/09/02 HTML / CSS
美国知名的摄影器材销售网站:Adorama
2017/02/01 全球购物
学习型党组织心得体会
2014/09/12 职场文书
小学体育组工作总结
2015/08/13 职场文书
小学语文新课改心得体会
2016/01/22 职场文书
人民币使用说明书
2019/04/17 职场文书
jQuery实现广告显示和隐藏动画
2021/07/04 jQuery