搜索
首页php教程php手册PHP的垃圾回收机制详解

PHP的垃圾回收机制详解

最近由于使用php编写了一个脚本,模拟实现了一个守护进程,因此需要深入理解php中的垃圾回收机制。本文参考了PHP手册。

 

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

 

php中变量存在于一个zval的变量容器中。结构如下:

 

 

 

 

 

类型

 

 

is_ref

 

refcount

 

 

 

 

 

zval中,除了存储变量的类型和值之外,还有is_ref字段和refcount字段。

 

is_ref:是个bool值,用来区分变量是否属于引用集合。什么意思呢,你可以这么认为:表示变量是否有一个以上的别名。 

refcount:计数器,表示指向这个zval变量容器的变量个数。 

两者之间有这么一个默认关系:当refcount值为1时,is_ref的值为false。因为refcount为1,此变量不可能有多个别名,也就不存在引用了。

 

安装xdebug拓展之后,可以利用xdebug_debug_zval打印出zval容器详情。

 

这里有一点需要注意,将一个变量 = 赋值给另一个变量时,不会立即为新变量分配内存空间,而是在原变量的zval中给refcount加1。 只有当原变量或者发生改变时,才会为新变量分配内存空间,同时原变量的refcount减 1 。当然,如果unset原变量,新变量直接就使用原变量的zval而不是重新分配。

 

 &引用赋值时,原变量的is_ref 变为1,refcount 加1.  如果给一个变量&赋值,之前 = 赋值的变量会分配空间。

 

 

 

 

$a = 1;

xdebug_debug_zval('a');

echo PHP_EOL;

$b = $a;

xdebug_debug_zval('a');

echo PHP_EOL;

 

$c = &$a;

xdebug_debug_zval('a');

echo PHP_EOL;

 

xdebug_debug_zval('b');

echo PHP_EOL;

?>

  运行结果如下:

 

a:(refcount=1, is_ref=0),int 1

 

a:(refcount=2, is_ref=0),int 1

 

a:(refcount=2, is_ref=1),int 1

 

b:(refcount=1, is_ref=0),int 1

 

 

 

 

 

上面描述的zval存储的是标量,那复合类型的数组是如何存储的呢?

 

 

$a = array( 'meaning' => 'life', 'number' => 42 );

xdebug_debug_zval( 'a' );

echo PHP_EOL;

class Test{

    public $a = 1;

    public $b = 2;

     

    function handle(){

        echo 'hehe';

    }

}

 

$test = new Test();

xdebug_debug_zval('test');

?>

  运行结果如下:

 

a:(refcount=1, is_ref=0),

 

array

  'meaning' => (refcount=1, is_ref=0),

string

 

'life' (length=4)

  'number' => (refcount=1, is_ref=0),

int

 

 42

test:(refcount=1, is_ref=0),

 

object(Test)[1]

  public 'a' => (refcount=2, is_ref=0),

int

 

 1

  public 'b' => (refcount=2, is_ref=0),

int

 

2

 

可以看出,数组用了比数组长度多1个zval存储。对象类似。下面给出了数组的存储形象表示

 

 

 

可以看到:数组分配了三个zval容器:a   meaning  number

 

现在看看所谓的环状引用是如何生成的

 

 

$a = array( 'one' );

$a[] =& $a;

xdebug_debug_zval( 'a' );

?>

  运行结果:

 

a:(refcount=2, is_ref=1),

 

array

  0 => (refcount=1, is_ref=0),

string

 

 'one' (length=3)

  1 => (refcount=2, is_ref=1), &array

 

a 和 1 的zval容器 是一样的。如下:

 

 

 

 

 

 

 

这样就形成了环状引用。

 

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

 

现在unset ($a),那么array的refcount减1变为1.现在无任何变量指向这个zval,而且这个zval的计数器为1,不会回收。

 

 

 

 

 

尽管不再有某个作用域中的任何符号指向这个结构(就是变量容器),由于数组元素“1”仍然指向数组本身,所以这个容器不能被清除 。因为没有另外的符号指向它,用户没有办法清除这个结构,结果就会导致内存泄漏。庆幸的是,php将在请求结束时清除这个数据结构,但是在php清除之前,将耗费不少空间的内存。如果你要实现分析算法,或者要做其他像一个子元素指向它的父元素这样的事情,这种情况就会经常发生。当然,同样的情况也会发生在对象上,实际上对象更有可能出现这种情况,因为对象总是隐式的被引用。

 

 

 

如果上面的情况发生仅仅一两次倒没什么,但是如果出现几千次,甚至几十万次的内存泄漏,这显然是个大问题。在长时间运行的脚本,比如请求基本上不会结束的守护进程时,就会出现问题,内存空间会不断耗费,导致内存不足而崩溃。

 

 

 

PHP5.3中,采用了专门的算法(比较复杂)。,来处理环状引用导致内存泄露的问题。

 

当一个zval可能为垃圾时,回收算法会把这个zval放入一个内存缓冲区。当缓冲区达到最大临界值时(最大值可以设置),回收算法会循环遍历所有缓冲区中的zval,判断其是否为垃圾,并进行释放处理。或者我们在脚本中使用gc_collect_cycles,强制回收缓冲区中的垃圾。

 

在php5.3的GC中,针对的垃圾做了如下说明:

 

1:如果一个zval的refcount增加,那么此zval还在使用,肯定不是垃圾,不会进入缓冲区

 

2:如果一个zval的refcount减少到0, 那么zval会被立即释放掉,不属于GC要处理的垃圾对象,不会进入缓冲区。

 

 3:如果一个zval的refcount减少之后大于0,那么此zval还不能被释放,此zval可能成为一个垃圾,将其放入缓冲区。PHP5.3中的GC针对的就是这种zval进行的处理。

 

 

 

 

 

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

 

 

 

开启垃圾回收机制后,针对内存泄露的情况,可以节省大量的内存空间,但是由于垃圾回收算法运行耗费时间,开启垃圾回收算法会增加脚本的执行时间。

 

下面是php手册中给的一个脚本

 

 

class Foo

{

    public $var = '3.1415962654';

}

 

$baseMemory = memory_get_usage();

 

for ( $i = 0; $i

{

    $a = new Foo;

    $a->self = $a;

    if ( $i % 500 === 0 )

    {

        echo sprintf( '�: ', $i ), memory_get_usage() - $baseMemory, "n";

    }

}

?>

  

 

针对这个脚本,给出了其在php5.2和5.3中内存的占用情况,如下图:

 

 

 

针对下面这个脚本

 

 

class Foo

{

    public $var = '3.1415962654';

}

 

for ( $i = 0; $i

{

    $a = new Foo;

    $a->self = $a;

}

 

echo memory_get_peak_usage(), "n";

?>

  

 

开启垃圾回收机制,相对于不开启的时候,脚本执行时间增加了7%

 

通常,PHP中的垃圾回收机制,仅仅在循环回收算法确实运行时会有时间消耗上的增加。但是在平常的(更小的)脚本中应根本就没有性能影响。

 

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热工具

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境