php 垃圾回收机制详解

理解PHP垃圾回收机制GC(Garbage Collection)之前,先了解一下变量的存储。

在php 手册中已经讲的很清楚了

基础定义:

https://www.php.net/manual/zh/features.gc.refcounting-basics.php

可以去这里翻翻手册看一下

 

简单的概述一下

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

 

 

 

所以在此不再过多的解释php变量。

 

在5.2及更早版本的PHP中,没有专门的垃圾回收器GC,引擎在判断一个变量空间是否能够被释放的时候是依据这个变量的zval的refcount的值,如果refcount为0,那么变量的空间可以被释放,否则就不释放,这是一种非常简单的GC实现,这就是引用计数内存机制

那为什么会需要垃圾回收呢?

因为在php中存在环状引用导致内存泄露,在之前的5.2 是回收不了的

问题:什么样的情况下存在环状引用呢?

答:当两个或多个对象互相引用形成环状后,内存对象的计数器则不会消减为0;这时候,这一组内存对象已经没用了,但是不能回收,从而导致内存泄露。

 

php5.3 新的垃圾回收机制

  php5.3版本之后引入根缓冲机制,即php启动时默认设置指定zval数量的根缓冲区(默认是10000),当php发现有存在循环引用的zval时,就会把其投入到根缓冲区,当根缓冲区达到配置文件中的指定数量(默认是10000)后,就会进行垃圾回收,以此解决循环引用导致的内存泄漏问题。

 

确认垃圾准则

1、如果引用计数减少到零,所在的变量容器将被清除(free),不属于垃圾;

2、如果一个zval的引用计数减少后大于0,那么他进入垃圾周期。其次,在一个垃圾周期中,通过检查引用计数是否减1,并且检查那些变量容器的引用次数是0,来发现哪部分是垃圾。

 

如何实现垃圾回收机制呢?

开启/关闭垃圾回收机制可以通过修改php配置实现,也可以在程序中使用gc_enable()-开启 和 gc_disable()-关闭。

 

unset函数的影响

unset只是断开一个变量到一块内存区域的连接,同时将该内存区域的引用计数-1;在上面的例子中,循环体内部,$a=new A(); unset($a);并不会将$a的引用计数减到零;

 

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

 

脚本执行结束的影响
脚本执行结束,该脚本中使用的所有内存都会被释放,不论是否有引用环。

 

注意:

PHP中的垃圾回收机制,仅仅在循环回收算法确实运行时会有时间消耗上的增加

 

总结:

1、以php的引用计数机制为基础(php5.3以前版本只有该机制);

2、同时使用根缓冲区机制,当php发现有存在循环引用的zval时,就会把其投入到根缓冲区,当根缓冲区达到配置文件中的指定数量后,就会进行垃圾回收,以此解决循环引用导致的内存泄漏问题(php5.3开始引入该机制);

 

posted @ 2021-06-14 17:32  方达达  阅读(157)  评论(0编辑  收藏  举报