php 垃圾回收机制 - Go语言中文社区

php 垃圾回收机制


php 垃圾定义

是否有变量名指向变量容器zval,如果没有则认为是垃圾,需要释放。

 

判断是否为垃圾

php5.3 前 refcount = 0 才认为是垃圾

产生内存泄漏主要真凶:环形引用

 

<?php $a = ['one']; $a[] = &$a; xdebug_debug_zval('a');

这样 $a数组就有了两个元素,一个索引为0,值为one字符串,另一个索引为1,为$a自身的引用。

图1

a:
(refcount=2, is_ref=1),
array (size=2)
  0 => (refcount=1, is_ref=0),string 'one' (length=3)
  1 => (refcount=2, is_ref=1),

 

<?php $a = ['one']; $a[] = &$a; unset($a);

 

图2

如果在小于php5.3的版本就会出现一个问题:$a已经不在符号表了,没有变量再指向此zval容器,用户已无法访问,但是由于数组的refcount变为1而不是0,导致此部分内存不能被回收从而产生了内存泄漏。

 

为解决环形引用导致的垃圾,产生了新的GC算法,遵守以下几个基本准则:

1.如果一个zval的refcount增加,那么此zval还在使用,不属于垃圾

2.如果一个zval的refcount减少到0, 那么zval可以被释放掉,不属于垃圾

3.如果一个zval的refcount减少之后大于0,那么此zval还不能被释放,此zval可能成为一个垃圾

 

>= 5.3 gc 机制 垃圾判断

1.zval容器中的每个元素refcount = 0,

2.zval容器的refcount = 0,

 

环形引用的数组,对数组中包含的每个元素的zval进行减1操作,之后如果发现数组自身的zval的refcount变成了0,那么可以判断这个数组是一个垃圾。

 

算法优化配置

可能会发现,每次都进行这样的操作好像会影响性能,是的,php做事情套路都是走批量的原则。

申请内存也是申请一大块,仅使用当前的一小部分剩下的等下回再用,避免多次申请。

这个gc算法也是这样,会有一个缓冲区的概念,等缓冲区满了才会一次性去给清掉。

 

开关配置

php.ini中设置 zend.enable_gc 项来开启或则关闭GC。

 

缓冲区配置

缓冲区默认可以放10,000个节点,当缓冲区满了才会清理。可以通过修改Zend/zend_gc.c中的GC_ROOT_BUFFER_MAX_ENTRIES 来改变这个数值,需要重新编译链接PHP

 

gc_enable() : 开启GC

gc_disable() : 关闭GC

gc_collect_cycles() : 在节点缓冲区未满的情况下强制执行垃圾分析算法

涉及到垃圾回收的知识点
1.unset函数

unset只是断开一个变量到一块内存区域的连接,同时将该内存区域的引用计数-1;内存是否回收主要还是看refount是否到0了,以及gc算法判断。

2.= null 操作;

a=null是直接将a=null是直接将a 指向的数据结构置空,同时将其引用计数归0。

3.脚本执行结束

 

A:为了避免每次变量的refcount减少的时候都调用GC的算法进行垃圾判断,此算法会先把所有前面准则3情况下的zval节点放入一个节点(root)缓冲区(root buffer),并且将这些zval节点标记成紫色,同时算法必须确保每一个zval节点在缓冲区中之出现一次。当缓冲区被节点塞满的时候,GC才开始开始对缓冲区中的zval节点进行垃圾判断。

B:当缓冲区满了之后,算法以深度优先对每一个节点所包含的zval进行减1操作,为了确保不会对同一个zval的refcount重复执行减1操作,一旦zval的refcount减1之后会将zval标记成灰色。需要强调的是,这个步骤中,起初节点zval本身不做减1操作,但是如果节点zval中包含的zval又指向了节点zval(环形引用),那么这个时候需要对节点zval进行减1操作。

C:算法再次以深度优先判断每一个节点包含的zval的值,如果zval的refcount等于0,那么将其标记成白色(代表垃圾),如果zval的refcount大于0,那么将对此zval以及其包含的zval进行refcount加1操作,这个是对非垃圾的还原操作,同时将这些zval的颜色变成黑色(zval的默认颜色属性)

D:遍历zval节点,将C中标记成白色的节点zval释放掉。
 

引起计数器+1的操作:
$a=new A();
$b=&$a;
引起计数器-1的操作:
unset($a);
引起计数器值0对象并释放内存:
$a=null;
内存相关的函数:
memory_get_usage()返回当前分配给你的 PHP 脚本的内存量,单位是字节(byte)
memory_get_peak_usage()返回分配给 PHP 内存的峰值
mysql_free_result() 手动释放mysql结果集内存
gc_enable();//开启gc自动回收
gc_collect_cycles();//执行一次回收周期
gc_disable();//关闭gc自动回收周期
xdebug_debug_zval(变量名)//加载完xdebug后,可以用这个函数查看变量的状态

 

参考文献:

https://blog.csdn.net/u011957758/article/details/76864400

https://www.jianshu.com/p/f16b92345f34

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_41282397/article/details/84969162
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-06-27 22:26:59
  • 阅读 ( 1315 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢