PHP进阶学习之垃圾回收机制详解


Posted in PHP onJune 18, 2019

本文实例讲述了PHP垃圾回收机制。分享给大家供大家参考,具体如下:

一、概念

垃圾回收机制是一种动态存储分配的方案。它会自动释放程序不再需要的已分配的内存块。垃圾回收机制可以让程序员不必过分关心程序内存分配,从而将更多的精力投入到业务逻辑。在现在的流行各种语言当中,垃圾回收机制是新一代语言所共有的特征,如Python、PHP、C#、Ruby等都使用了垃圾回收机制。

二、PHP垃圾回收机制

1、在PHP5.3版本之前,使用的垃圾回收机制是单纯的“引用计数”。即:
①每个内存对象都分配一个计数器,当内存对象被变量引用时,计数器+1;
②当变量引用撤掉后(执行unset()后),计数器-1;
③当计数器=0时,表明内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成。
并且PHP在一个生命周期结束后就会释放此进程/线程所占的内容,这种方式决定了PHP在前期不需要过多考虑内存的泄露问题。 

但是当两个或多个对象互相引用形成环状后,内存对象的计数器则不会消减为0;这时候,这一组内存对象已经没用了,但是不能回收,从而导致内存泄露的现象。
php5.3开始,使用了新的垃圾回收机制,在引用计数基础上,实现了一种复杂的算法,来检测内存对象中引用环的存在,以避免内存泄露。

2、随着PHP的发展,PHP开发者的增加以及其所承载的业务范围的扩大,在PHP5.3中引入了更加完善的垃圾回收机制,新的垃圾回收机制解决了无法处理循环的引用内存泄漏问题。

如官方文档所说:每个php变量存在一个叫"zval"的变量容器中。一个zval变量容器,除了包含变量的类型和值,还包括两个字节的额外信息。第一个是"is_ref",是个bool值,用来标识这个变量是否是属于引用集合(reference set)。通过这个字节,php引擎才能把普通变量和引用变量区分开来,由于php允许用户通过使用&来使用自定义引用,zval变量容器中还有一个内部引用计数机制,来优化内存使用。第二个额外字节是"refcount",用以表示指向这个zval变量容器的变量(也称符号即symbol)个数。所有的符号存在一个符号表中,其中每个符号都有作用域(scope)。简单的理解如下图所示:

PHP进阶学习之垃圾回收机制详解

如官方文档所说,可以使用Xdebug来检查引用计数情况:

<?php
$a = "new string";
$c = $b = $a;
xdebug_debug_zval( 'a' );
unset( $b, $c );
xdebug_debug_zval( 'a' );
?>

以上例程会输出:

a: (refcount=3, is_ref=0)='new string'
a: (refcount=1, is_ref=0)='new string'

注意:从PHP7的NTS版本开始,以上例程的引用将不再被计数,即$c=$b=$a之后a的引用计数也是1.具体分类如下:
在PHP 7中,zval可以被引用计数或不被引用。在zval结构中有一个标志确定了这一点。
对于null,bool,int和double的类型变量,refcount永远不会计数;
②对于对象、资源类型,refcount计数和php5的一致;
对于字符串,未被引用的变量被称为“实际字符串”。而那些被引用的字符串被重复删除(即只有一个带有特定内容的被插入的字符串)并保证在请求的整个持续时间内存在,所以不需要为它们使用引用计数;如果使用了opcache,这些字符串将存在于共享内存中,在这种情况下,您不能使用引用计数(因为我们的引用计数机制是非原子的);
对于数组,未引用的变量被称为“不可变数组”。其数组本身计数与php5一致,但是数组里面的每个键值对的计数,则按前面三条的规则(即如果是字符串也不在计数);如果使用opcache,则代码中的常量数组文字将被转换为不可变数组。再次,这些生活在共享内存,因此不能使用refcounting。

我们的demo例子如下:

<?php
echo '测试字符串引用计数';
$a = "new string";
$b = $a;
xdebug_debug_zval( 'a' );
unset( $b);
xdebug_debug_zval( 'a' );
$b = &$a;
xdebug_debug_zval( 'a' );
echo '测试数组引用计数';
$c = array('a','b');
xdebug_debug_zval( 'c' );
$d = $c;
xdebug_debug_zval( 'c' );
$c[2]='c';
xdebug_debug_zval( 'c' );
echo '测试int型计数';
$e = 1;
xdebug_debug_zval( 'e' );

看到的输出如下:

PHP进阶学习之垃圾回收机制详解

可以参考:https://stackoverflow.com/questions/34764119/confusion-about-php-7-refcount

三、回收周期

默认的,PHP的垃圾回收机制是打开的,然后有个php.ini设置允许你修改它:zend.enable_gc 。

当垃圾回收机制打开时,算法会判断每当根缓存区存满时,就会执行循环查找。根缓存区有固定的大小,默认10,000,可以通过修改PHP源码文件Zend/zend_gc.c中的常量GC_ROOT_BUFFER_MAX_ENTRIES,然后重新编译PHP,来修改这个值。当垃圾回收机制关闭时,循环查找算法永不执行,然而,根将一直存在根缓冲区中,不管在配置中垃圾回收机制是否激活。

除了修改配置zend.enable_gc ,也能通过分别调用gc_enable() 和 gc_disable()函数在运行php时来打开和关闭垃圾回收机制。调用这些函数,与修改配置项来打开或关闭垃圾回收机制的效果是一样的。即使在可能根缓冲区还没满时,也能强制执行周期回收。你能调用gc_collect_cycles()函数达到这个目的。这个函数将返回使用这个算法回收的周期数。

允许打开和关闭垃圾回收机制并且允许自主的初始化的原因,是由于你的应用程序的某部分可能是高时效性的。在这种情况下,你可能不想使用垃圾回收机制。当然,对你的应用程序的某部分关闭垃圾回收机制,是在冒着可能内存泄漏的风险,因为一些可能根也许存不进有限的根缓冲区。因此,就在你调用gc_disable()函数释放内存之前,先调用gc_collect_cycles()函数可能比较明智。因为这将清除已存放在根缓冲区中的所有可能根,然后在垃圾回收机制被关闭时,可留下空缓冲区以有更多空间存储可能根。

四、性能影响

1、内存占用空间的节省

首先,实现垃圾回收机制的整个原因是为了一旦先决条件满足,通过清理循环引用的变量来节省内存占用。在PHP执行中,一旦根缓冲区满了或者调用gc_collect_cycles() 函数时,就会执行垃圾回收。

2、执行时间增加

垃圾回收影响性能的第二个领域是它释放已泄漏的内存耗费的时间。
通常,PHP中的垃圾回收机制,仅仅在循环回收算法确实运行时会有时间消耗上的增加。但是在平常的(更小的)脚本中应根本就没有性能影响。

3、在平常脚本中有循环回收机制运行的情况下,内存的节省将允许更多这种脚本同时运行在你的服务器上。因为总共使用的内存没达到上限。
这种好处在长时间运行脚本中尤其明显,诸如长时间的测试套件或者daemon脚本此类。同时,对通常比Web脚本运行时间长的脚本应用程序,新的垃圾回收机制,应该会大大改变一直以来认为内存泄漏问题难以解决的看法。

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
mysql下创建字段并设置主键的php代码
May 16 PHP
header跳转和include包含问题详解
Sep 08 PHP
关于PHP内存溢出问题的解决方法
Jun 25 PHP
ThinkPHP做文字水印时提示call an undefined function exif_imagetype()解决方法
Oct 30 PHP
PHP PDO fetch 模式各种参数的输出结果一览
Jan 07 PHP
php图像处理类实例
Jul 28 PHP
制作个性化的WordPress登陆界面的实例教程
May 21 PHP
PHP yii实现model添加默认值的方法(两种方法)
Nov 10 PHP
PHP实现函数内修改外部变量值的方法示例
Dec 28 PHP
PHP实现的操作数组类库定义与用法示例
May 24 PHP
php中isset与empty函数的困惑与用法分析
Jul 05 PHP
php正则表达式使用方法整理集合
Jan 31 PHP
PHP进阶学习之命名空间基本用法分析
Jun 18 #PHP
PHP进阶学习之反射基本概念与用法分析
Jun 18 #PHP
Laravel使用RabbitMQ的方法示例
Jun 18 #PHP
thinkphp整合系列之极验滑动验证码geetest功能
Jun 18 #PHP
PHP Trait代码复用类与多继承实现方法详解
Jun 17 #PHP
php设计模式之装饰模式应用案例详解
Jun 17 #PHP
php设计模式之策略模式应用案例详解
Jun 17 #PHP
You might like
phpMyAdmin2.11.6安装配置方法
2008/08/24 PHP
PHP学习之数组值的操作
2011/04/17 PHP
thinkphp 验证码 的使用小结
2017/05/07 PHP
js 未结束的字符串常量错误解决方法
2010/06/13 Javascript
分享一个用Mootools写的鼠标滑过进度条改变进度值的实现代码
2011/12/12 Javascript
给页面渲染时间加速 干掉Dom Level 0 Event
2012/12/19 Javascript
js 自定义个性下拉选择框示例
2013/08/20 Javascript
js实现日期级联效果
2014/01/23 Javascript
jQuery学习笔记之基础中的基础
2015/01/19 Javascript
jQuery消息提示框插件Tipso
2015/05/04 Javascript
原生js实现jquery函数animate()动画效果的简单实例
2016/08/21 Javascript
angularjs ocLazyLoad分步加载js文件实例
2017/01/17 Javascript
jQuery实现动态添加、删除按钮及input输入框的方法
2017/04/27 jQuery
AngularJS的$location使用方法详解
2017/10/19 Javascript
vue 2.8.2版本配置刚进入时候的默认页面方法
2018/09/21 Javascript
Vue服务端渲染实践之Web应用首屏耗时最优化方案
2019/03/22 Javascript
[04:29]2014DOTA2国际邀请赛 主赛事第三日TOPPLAY
2014/07/21 DOTA
python 线程的暂停, 恢复, 退出详解及实例
2016/12/06 Python
Python中扩展包的安装方法详解
2017/06/14 Python
python urllib爬取百度云连接的实例代码
2017/06/19 Python
python实现八大排序算法(2)
2017/09/14 Python
解决Python 中英文混输格式对齐的问题
2018/07/16 Python
给大家整理了19个pythonic的编程习惯(小结)
2019/09/25 Python
Pycharm和Idea支持的vim插件的方法
2020/02/21 Python
python ImageDraw类实现几何图形的绘制与文字的绘制
2020/02/26 Python
家得宝加拿大家装网上商店:The Home Depot加拿大
2016/08/27 全球购物
暑假实习求职信范文
2013/09/22 职场文书
2015年元旦活动总结
2014/05/09 职场文书
村居抓节水倡议书
2014/05/19 职场文书
家长学校培训材料
2014/08/20 职场文书
大学生第一学年自我鉴定
2014/09/12 职场文书
2014年英语教学工作总结
2014/12/17 职场文书
房地产置业顾问岗位职责
2015/04/11 职场文书
企业团队精神心得体会
2016/01/19 职场文书
《遗弃》开发商删推文要跑路?官方回应:还在开发
2022/04/03 其他游戏
Vue ECharts实现机舱座位选择展示功能
2022/05/15 Vue.js