深入解析PHP垃圾回收机制对内存泄露的处理

yipeiwu_com6年前PHP代码库

上次说到了refcount和is_ref,这里来说说内存泄露的情况

复制代码 代码如下:

$a = array(1, 2, &$a);
unset($a);

在老的PHP版本中,这里就会出现内存泄露,分析如下:

执行第一行,可以知道$a和$a[2]指向的zval refcount=2,is_ref=1

然后执行第二行,$a将会从符号表中被删除,同时指向的zval的refcount--,此时refcount=1,因为refcount!=0,故此zval不会被当做垃圾回收,但是此时我们却失去了$a[2]指向这个zval的入口,因此这个zval成了一块内存垃圾

同样的道理可以发生在类内部引用里,例如

复制代码 代码如下:

$a = new Man();
$a->self = &$a;
unset($a);

那么如何解决这种问题呢,新的GC机制采用了一个算法来解决这个问题

PHP有一个root buffer用来存储zval的节点信息,当root buffer满了或者手动调用gc函数时,GC算法启动

对于一个数组或者类类型的zval而言,在垃圾回收机制启动时,算法会对该zval的数组/类内部的元素/成员的zval进行一次遍历并将refcount减1,如果说遍历完成后该zval的refcount被减为0,则说明这个zval是一个内存垃圾,他将被销毁,见下面的例子

复制代码 代码如下:

$a = array(1, 2, &$a, &$a);
unset($a);

容易知道$a指向的zval,假设为z1的refcount=3,is_ref=1

当unset($a)执行的时候,$a就已经从符号表中删去,同时我们也失去了访问z1的入口,此时z1 refcount=2,is_ref=1

当GC启动时,会对该z1的数组元素的zval的refcount进行遍历减1,遍历到a[2]时,z1 refcount--, a[3]时 z1 refcount--,此时z1 refcount = 0,即可将z1标记为内存垃圾,算法后将其回收

总结来说可以这么表述:若一个数组类型的zval,对他的元素zval进行一次遍历,同时将遍历到的zval的refcount--,如果最后refcount=0的zval,就是垃圾,需要被回收

相关文章

php中删除数组的第一个元素和最后一个元素的函数

对于一个php数组,该如何删除该数组的第一个元素或者最后一个元素呢?其实这两个过程都可以通过php自带的函数 array_pop 和 array_shift 来完成,下面就具体介绍一下如...

php中使用parse_url()对网址进行解析的实现代码(parse_url详解)

PHP 解析 URL函数: parse_url详解 parse_url — 解析 URL,返回其组成部分 说明 array parse_url ( string $url ) 本函数解析...

PHP实现找出数组中出现次数超过数组长度一半的数字算法示例

本文实例讲述了PHP实现找出数组中出现次数超过数组长度一半的数字算法。分享给大家供大家参考,具体如下: <?php * 算法要求:数组中有一个数字出现的次数超过了数组长...

php 小乘法表实现代码

复制代码 代码如下:<?php for($i=1;$i<10;$i++) { for($j=1;$j<=$i;$j++) { $result = $i*$j; if(s...

PHP判断一个数组是另一个数组子集的方法详解

PHP判断一个数组是另一个数组子集的方法详解

本文实例讲述了PHP判断一个数组是另一个数组子集的方法。分享给大家供大家参考,具体如下: 前言 今天完成一个算法的过程中,有几个需求模块,其中就有判断$a数组是否是$b数组的子集,可能最...