嗨,老铁,欢迎来到我的博客!

如果觉得我的内容还不错的话,可以关注下我在 segmentfault.com 上的直播。我主要从事 PHP 和 Java 方面的开发,《深入 PHP 内核》作者之一。

[视频直播] PHP 进阶之路 - 亿级 pv 网站架构的技术细节与套路 直播中我将毫无保留的分享我这六年的全部工作经验和踩坑的故事,以及会穿插着一些面试中的 考点难点加分点

周梦康 发表于 2016-02-25 3221 次浏览 标签 : php

免费领取阿里云优惠券 我的直播 - 《PHP 进阶之路》

内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元,直到程序结束。其实说白了就是该内存空间使用完毕之后未回收。

初步认识 PHP 变量结构体

我们下面的讲解都是以 PHP 5.3 做为基础。

PHP 变量的结构体实现

typedef struct _zval_struct zval;
...
struct _zval_struct {
    zvalue_value value;     /* 值 */
    zend_uint refcount__gc;
    zend_uchar type;    /* 类型 */
    zend_uchar is_ref__gc;
};

变量实际的值是存在zvalue_value这个结构体中的。

这里复习下zval结构体是为了能更加方便阅读下面的绘图和函数xdebug_debug_zval()结果的理解。

如果你对这块知识比较模糊,可以看 TIPI 的详细讲解。

数组内部的递归引用,删除数组之后导致的内存泄漏

比如,现有如下数组$a,调用函数xdebug_debug_zval()显示refcountis_ref的值。

<?php
$a = array('one');
xdebug_debug_zval('a');
?>

打印结果如下

a: (refcount=1, is_ref=0)=array (
	0 => (refcount=1, is_ref=0)='one'
)

PHP 内存泄漏举例

先给数组a添加一个元素,并且该元素是引用其自身

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

打印结果如下

a: (refcount=2, is_ref=1)=array (
   0 => (refcount=1, is_ref=0)='one',
   1 => (refcount=2, is_ref=1)=...
)

下标1的值是...表示发生了递归操作,引用的是其自身

如果我们在执行完上面的代码后,对变量$a调用unset,那么变量$a和数组元素 "1" 所指向的变量容器的引用次数减 1,从 2 变成 1 。尽管不再有某个作用域中的任何符号指向这个结构(就是变量容器),由于数组元素 "1" 仍然指向数组本身,所以这个容器不能被清除 。因为没有另外的符号指向它,用户没有办法清除这个结构,结果就会导致内存泄漏。

虽然 PHP 将在脚本执行结束时清除这个数据结构,但是在 PHP 清除之前,将耗费不少内存。如果你要实现分析算法,或者要做其他像一个子元素指向它的父元素这样的事情,这种情况就会经常发生。当然,同样的情况也会发生在对象上,实际上对象更有可能出现这种情况,因为对象总是隐式的被引用。

如果上面的情况发生仅仅一两次倒没什么,但是如果出现几千次,甚至几十万次的内存泄漏,这显然是个大问题。这样的问题往往发生在长时间运行的脚本中。


如果你阅读了此篇,我觉得 PHP 的垃圾回收也应该了解下(回收泄漏的内存)

嗨,老铁,欢迎来到我的博客!

如果觉得我的内容还不错的话,可以关注下我在 segmentfault.com 上的直播。我主要从事 PHP 和 Java 方面的开发,《深入 PHP 内核》作者之一。

[视频直播] PHP 进阶之路 - 亿级 pv 网站架构的技术细节与套路 直播中我将毫无保留的分享我这六年的全部工作经验和踩坑的故事,以及会穿插着一些面试中的 考点难点加分点

评论列表