内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元,直到程序结束。其实说白了就是该内存空间使用完毕之后未回收。
初步认识 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()
显示refcount
和is_ref
的值。
<?php $a = array('one'); xdebug_debug_zval('a'); ?>
打印结果如下
a: (refcount=1, is_ref=0)=array ( 0 => (refcount=1, is_ref=0)='one' )
先给数组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 的垃圾回收也应该了解下(回收泄漏的内存)