浅析PHP7 的垃圾回收机制


Posted in PHP onSeptember 06, 2019

垃圾回收机制

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

垃圾的产生

PHP7 中复杂类型,像字符串、数组、对象等的数据结构中,头部都有一个 gc, 这个 gc 的作用就是用来对垃圾回收的支持。当变量赋值、传递时,会增加 value 的引用数, unset、return 等释放变量时再减掉引用数,减掉后如果发现 refcount 变为 0 则直接释放 value,这是变量的基本回收过程。

不过有一种问题是这个机制无法解决的,就是循环引用的问题。

什么是循环引用呢? 简单说就是变量的内部里存的 value 又引用了变量自身。 这种比较经常发生在数组和对象类型的变量上。

这里先讲一下引用,即 zend_reference 这个类型,这个是 PHP7 新增的变量类型,当对变量使用 “&” 操作时,会创建新的中间结构体 zend_reference,这个结构体会真正的指向对应的 value 结构。

举个例子:

// 当进行如下赋值操作时
$a = 'hello'; // $a -> zend_string
$b = $a; // $b,$a -> zend_string
$c = &$b; // $c,$b -> zval(type = IS_REFERENCE, refcount = 2) -> zend_string

最终会变成如下这样:

 浅析PHP7 的垃圾回收机制

即 $b 和 $c 的 zval 是通过中间结构体 zend_reference 再指向最终的 zend_string

回到循环引用的问题,举个数组循环引用例子:

$arr = [1];
$a[] = &$a;
unset($a);

使用 & 操作之后,变量 a 就变成了引用类型且引用计数 refcount 为 2,而又赋值给自己里面的元素,即变量 a 变成了自己引用自己。

具体如下如所示:

 浅析PHP7 的垃圾回收机制

当 unset 之后就变成下图这样:

 浅析PHP7 的垃圾回收机制

即 $a 所在的 zval 类型已经变成了 IS_UNDEF 了,zend_reference 结构体的引用计数减 1,但是仍然大于 0,这时候,这部分结构体就变成了垃圾,对此不处理的话,就可能会造成内存泄露。这里就需要垃圾收集器将这部分收集到缓冲区,之后进行回收处理。

回收过程

如果当变量的 refcount 减小后大于 0,PHP 并不会立即对这个变量进行垃圾鉴定和回收,而是放入一个缓冲区中,等这个缓冲区满了以后(10000 个值)再统一进行处理,加入缓冲区的是变量 zend_value 里的 gc,目前垃圾只会出现在数组和对象两种类型中,数组的情况上面已经介绍了,对象的情况则是成员属性引用对象本身导致的,其它类型不会出现这种变量中的成员引用变量自身的情况,所以垃圾回收只会处理这两种类型的变量。

gc 的结构 zend_refcounted_h 具体如下:

typedef struct _zend_refcounted_h {
  uint32_t     refcount; // 记录 zend_value 的引用数
  union {
    struct {
      zend_uchar  type, // zend_value的类型, 与zval.u1.type一致
      zend_uchar  flags, 
      uint16_t   gc_info // GC信息,记录在 gc 池中的位置和颜色,垃圾回收的过程会用到
    } v;
    uint32_t type_info;
  } u;
} zend_refcounted_h;

一个变量只能加入一次缓冲区,为了防止重复加入,变量加入后会把 zend_refcounted_h.gc_info 置为 GC_PURPLE,即标为紫色,后续不会重复插入。

垃圾缓冲区是一个双向链表,等到缓存区满了以后则启动垃圾检查过程:遍历缓冲区,对当前变量的所有成员进行遍历,然后把成员的 refcount 减 1 (如果成员还包含子成员则也进行递归遍历,即深度优先遍历),最后再检查当前变量的引用,如果减为了 0 则为垃圾。这个算法的原理核心是:垃圾是由于成员引用自身导致的,那么就对所有的成员减一遍引用,如果发现最后变量本身的 refcount 变为了 0 则就表明其引用全部来自自身成员,即其他任何地方都不再使用它,那么它就是垃圾,需要被回收掉。反之说明不是垃圾,需要将其从缓冲区移出去。具体的过程如下:

(1) 从缓冲区链表的 roots 开始遍历,把当前 value 标为灰色 (zend_refcounted_h.gc_info 置为 GC_GREY),然后对当前 value 的成员进行深度优先遍历,把成员 value 的 refcount 减 1,并且也标为灰色;

(2) 重复遍历缓冲区链表,检查当前 value 引用是否为 0,为 0 则表示确实是垃圾,把它标为白色(GC_WHITE),如果不为 0 则排除了引用全部来自自身成员的可能,表示还有外部的引用,并不是垃圾,这时候因为步骤(1)对成员进行了 refcount 减 1 操作,需要再还原回去,对所有成员进行深度遍历,把成员 refcount 加 1,同时标为黑色;

(3) 再次遍历缓冲区链表,将非 GC_WHITE 的节点从 roots 链表中移出(移到待释放的列表),最终 roots 链表中全部为真正的垃圾,最后将这些垃圾清除。

总结

以上所述是小编给大家介绍的PHP7 的垃圾回收机制,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

PHP 相关文章推荐
怎么样可以把 phpinfo()屏蔽掉?
Nov 24 PHP
php中通过虚代理实现延迟加载的实现代码
Jun 10 PHP
如何批量替换相对地址为绝对地址(利用bat批处理实现)
May 27 PHP
PHP迭代器的内部执行过程详解
Nov 12 PHP
PHP面向对象教程之自定义类
Jun 10 PHP
Linux下php5.4启动脚本
Aug 03 PHP
Laravel框架学习笔记(一)环境搭建
Oct 15 PHP
PHP微信开发用Cache 解决数据缓存
Jul 11 PHP
php实现的统计字数函数定义与使用示例
Jul 26 PHP
yii2.0整合阿里云oss上传单个文件的示例
Sep 19 PHP
PHP使用文件锁解决高并发问题示例
Mar 29 PHP
如何解决PHP获取不到SESSION信息之一般情况
Oct 10 PHP
PHP的介绍以及优势详细分析
Sep 05 #PHP
laravel5.1框架基础之Blade模板继承简单使用方法分析
Sep 05 #PHP
Laravel5.4框架中视图共享数据的方法详解
Sep 05 #PHP
php生成微信红包数组的方法
Sep 05 #PHP
Laravel框架创建路由的方法详解
Sep 04 #PHP
Laravel框架查询构造器 CURD操作示例
Sep 04 #PHP
Yii框架操作cookie与session的方法实例详解
Sep 04 #PHP
You might like
使用php+xslt在windows平台上
2006/10/09 PHP
php中获取远程客户端的真实ip地址的方法
2011/08/03 PHP
php ctype函数中文翻译和示例
2014/03/21 PHP
php简单实现发送带附件的邮件
2015/06/10 PHP
PHP批量去除BOM头代码分享
2015/06/26 PHP
Yii 框架使用Forms操作详解
2020/05/18 PHP
前端开发部分总结[兼容性、DOM操作、跨域等](持续更新)
2010/03/04 Javascript
JavaScript 学习初步 入门教程
2010/03/25 Javascript
在IE和VB中支持png图片透明效果的实现方法(vb源码打包)
2011/04/01 Javascript
兼容所有浏览器的js复制插件Zero使用介绍
2014/03/19 Javascript
js实现简单排列组合的方法
2016/01/27 Javascript
论JavaScript模块化编程
2016/03/07 Javascript
jQuery的ajax和遍历数组json实例代码
2016/08/01 Javascript
TypeScript入门-接口
2017/03/30 Javascript
微信小程序picker组件下拉框选择input输入框的实例
2017/09/20 Javascript
详解Angular-ui-BootStrap组件的解释以及使用
2018/07/13 Javascript
Vue中对拿到的数据进行A-Z排序的实例
2018/09/25 Javascript
Windows上node.js的多版本管理工具用法实例分析
2019/11/06 Javascript
关于IDEA中的.VUE文件报错 Export declarations are not supported by current JavaScript version
2020/10/17 Javascript
[07:08]2014DOTA2西雅图国际邀请赛 小组赛7月11日TOPPLAY
2014/07/11 DOTA
详解python OpenCV学习笔记之直方图均衡化
2018/02/08 Python
Python爬虫 scrapy框架爬取某招聘网存入mongodb解析
2019/07/31 Python
如何理解python中数字列表
2020/05/29 Python
Python数据可视化实现多种图例代码详解
2020/07/14 Python
CSS3中:nth-child和:nth-of-type的区别深入理解
2014/03/10 HTML / CSS
韩国美国时尚服装和美容在线全球市场:KOODING
2018/11/07 全球购物
白俄罗斯大卖场:21vek.by
2019/07/25 全球购物
DOM和JQuery对象有什么区别
2016/11/11 面试题
大学生实习自我鉴定
2013/12/11 职场文书
大学应届生的自我评价
2014/03/06 职场文书
创业融资计划书
2014/04/25 职场文书
应届硕士毕业生自荐信
2014/05/26 职场文书
工地宣传标语
2014/06/18 职场文书
Html分层的box-shadow效果的示例代码
2021/03/30 HTML / CSS
解决SpringCloud Feign传对象参数调用失败的问题
2021/06/23 Java/Android
vue-cli3.x配置全局的scss的时候报错问题及解决
2022/04/30 Vue.js