一、引用计数基本知识
每个php变量存在一个叫“zval”的变量容器中,一个zval变量容器,除了包含变量的类型和值,还包括两个字节的额外信息。第一个是“is_ref",是个bool值,用来标识这个变量是否属于引用集合。通过这个字节,php引擎才能把普通变量和引用变量区分开来,由于php允许用户通过使用&来使用自定义引用,zval变量容器中还有一个内部引用计数机制来优化内存使用。第二个额外字节是”refcount“,用以表示指向这个zval变量容器的变量个数。所有的符号存在一个符号表中,其中每个符号都有作用域(脚本中的函数或方法也都有作用域)。
① 当一个变量被赋常量值时,就会变成一个zval变量容器,如下:
$a = "new string"; xdebug_debug_zval('a'); //输出结果如下: a: (refcount=1, is_ref=0)='new string'
在上例中,新的变量a,是当前作用域中生成的。并且生成了类型为String和值为new string的变量容器。在额外的两个字节信息中,”is_ref"被默认设置为FALSE,因为没有任何自定义的引用生成。“refcount"被设定为1,因为这里只有一个变量使用这个变量容器。注意到当”refcount"的值是1时,“is_ref”的值总是false。
②当一个变量赋值给另一变量将增加引用次数(refcount),如下:
$a = "new string"; $b = $a; xdebug_debug_zval( 'a' ); //输出: a: (refcount=2, is_ref=0)='new string'
这时,引用次数是2,因为同一个变量容器被变量a和变量b关联。当没必要时,php不会去复制已生成的变量容器。变量容器在“refcount”变成0时就被销毁。
③当任何关联到某个变量容器的变量离开它的作用域(比如:函数执行结束),或者对变量调用了函数unset()时,“refcount”就会减1,如下:
$a = "new string"; $c = $b = $a; xdebug_debug_zval( 'a' ); unset( $b, $c ); xdebug_debug_zval( 'a' ); //输出结果 a: (refcount=3, is_ref=0)='new string' a: (refcount=1, is_ref=0)='new string'
如果再执行unset($a),包含类型和值的这个变量容器就会从内存中删除。
二、复合类型
① 当考虑像array和Object这样的符合类型时,事情就复杂了。与标量类型的值不同,array和Object类型的变量把他们的成员或属性存在自己的符号表中。这意味着下面的例子将生成三个zval变量容器:
$a = array( 'meaning' => 'life', 'number' => 42 ); xdebug_debug_zval( 'a' ); //输出如下: a: (refcount=1, is_ref=0)=array ( 'meaning' => (refcount=1, is_ref=0)='life', 'number' => (refcount=1, is_ref=0)=42 )
结构图如下:
这三个zval变量容器是:a,meaning和number。增加和减少"refcount"的规则和上面提到的一样。
②下面,再在数组中添加一个元素,并把它的值设置为数组中已存在元素的值:
$a = array( 'meaning' => 'life', 'number' => 42 ); $a['life'] = $a['meaning']; xdebug_debug_zval( 'a' ); //输出如下: a: (refcount=1, is_ref=0)=array ( 'meaning' => (refcount=2, is_ref=0)='life', 'number' => (refcount=1, is_ref=0)=42, 'life' => (refcount=2, is_ref=0)='life' )
结构图如下:
从以上的xdebug输出信息,我们看到原有的数组元素和新添加的数组元素关联到同一个“refcount”2的zval变量容器。尽管xdebug的输出显示两个值为"life“的zval变量容器,其实是同一个。函数xdebug_debug_zval()不显示这个信息,但是通过显示内存指针信息就可以看到。
③删除数组中的一个元素,就是类似于从作用域中删除一个变量。删除后,数组中的这个元素所在的容器的refcount值减少,同样当refcount为0时,这个变量容器就从内存中被删除,看下面的例子:
$a = array( 'meaning' => 'life', 'number' => 42 ); $a['life'] = $a['meaning']; unset( $a['meaning'], $a['number'] ); xdebug_debug_zval( 'a' ); //输出信息为 a: (refcount=1, is_ref=0)=array ( 'life' => (refcount=1, is_ref=0)='life' )
④ 当我们添加一个数组本身作为这个数组的元素时,事情就变得有趣,看下面的例子:
$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)=... )
结构图如下:
能看到数组变量a同时也是这个数组的第二个元素(1)指向的变量容器中refcount为2。上面的输出结构中的”..."说明发生了递归操作,显然在这种情况下意味着"..."指向原始数组。
⑤对一个变量调用unset,将删除这个符号,且它指向的变量容器中的引用次数也减1.所以,如果我们在执行完④的代码后,对变量$a调用unset,那么变量$a和数组元素”1“所指向的变量容器的引用次数减1,从2变成1,如下例子:
(refcount=1, is_ref=1)=array ( 0 => (refcount=1, is_ref=0)='one', 1 => (refcount=1, is_ref=1)=... )
结构图如下:
三、清理变量容器的问题----内存泄露
从(二⑤)中可以看出,尽管不再有某个作用域中的任何符号指向这个结构(就是变量容器),由于数组元素”1“仍然指向数组本身,所以这个容器不会被清楚。因为没有另外的符号指向它,用户没有办法清楚这个结构,结果就会导致内存泄露。
庆幸的是,php将在请求结束时清楚这个数据结构,但是在php清楚之前,将耗费不少空间内存。如果你要实现分析算法,或者要做其他像一个子元素指向它的父元素这种的事情,这种情况就会经常发生。当然,同样的情况也会发生在对象上,实际上对象更可能出现这种情况,因为对象总是隐式的被引用。
如果上面的情况仅仅发生一两次倒也没有什么,但是如果出现几千次或更多,这显然是个大问题。在长时间运行的脚本,例如请求基本上不会结束的守护进程或者单元测试中的大的套件中,就有可能消耗掉大量的内存。
相关推荐
在早期的PHP版本中,垃圾回收是基于引用计数的,即当一个变量的引用计数降为0时,认为该变量不再使用,进而进行回收。然而,这种方法无法处理循环引用的问题,即两个或更多变量相互引用,但没有其他外部引用,导致...
### PHP垃圾回收算法原理 #### 引言 在软件开发领域,自动存储回收技术通过引用计数(reference counting)和追踪(tracing)两种方法得以实现。本文档探讨了一种结合引用计数与并发循环收集(concurrent cycle ...
在PHP5.3以后的版本,引入了一种更复杂的垃圾回收算法,称为“引用计数+可达性检测”,它结合了引用计数和追踪循环引用的方法,以更有效地回收内存。 在实际编程中,理解这些机制对于优化性能和避免内存泄漏至关...
然而,单纯的引用计数不能解决所有问题。例如,当两个变量通过引用关联时,即使它们指向同一个`zval`,`refcount`也会增加。这时就需要`is_ref`字段发挥作用。当创建一个引用时,`is_ref`会被设置为1,表明这个变量...
引用计数是一种常见的垃圾回收策略,广泛应用于Perl、Python、PHP等语言。每个对象都有一个引用计数,当计数为零时,对象被认为是可以被回收的。然而,引用计数存在一个问题,即无法处理循环引用的情况。在循环引用...
PHP7中的垃圾回收主要依靠引用计数(reference counting)算法来实现。在PHP7中,每个值(value)都有一个引用计数(refcount),该计数表示有多少变量指向该值。当变量被赋值或者传递时,引用计数增加;当变量被...
在PHP中,垃圾回收主要依赖于引用计数(reference counting)这一策略。每创建一个对象,它都会有一个内置的引用计数器。当一个变量指向该对象时,计数器增加;当变量被解除引用或设置为NULL时,计数器减少。如果一...
PHP垃圾回收(Garbage Collection, 简称GC)机制是用于管理程序内存的一种自动机制,它主要负责识别并释放不再使用的内存空间,以防止内存泄露。内存泄露是指程序中已分配的内存没有被正确地释放,导致系统资源浪费...
本文实例讲述了PHP垃圾回收机制。分享给大家供大家参考,具体如下: 一、概念 垃圾回收机制是一种动态存储分配的方案。它会自动释放程序不再需要的已分配的内存块。垃圾回收机制可以让程序员不必过分关心程序内存...
本文主要和大家分享掌握php垃圾回收机制的知识,希望能帮助到大家。 引用计数基本知识 官网的解答如下 每个php变量存在一个叫”zval”的变量容器中一个zval变量容器,除了包含变量的类型和值 ,还包括两个字节的额外...
PHP中的垃圾回收机制是一种自动内存管理技术,它帮助PHP在运行时...总结起来,PHP的垃圾回收机制确保了内存的高效使用和自动管理,但同时也需要注意正确的使用引用计数和内存申请策略,以便写出更加高效和安全的代码。
PHP垃圾回收机制是指PHP语言中,为了管理内存资源,自动回收不再使用的内存空间的机制。这种机制让程序员在编程时可以少关注内存分配和回收的问题。然而,在面向对象编程(OOP)中,有时需要程序员显式地销毁某些...
php 的垃圾回收机制 PHP 可以自动进行内存管理,清除不需要的对象。 PHP 使用了引用计数 (reference counting) GC 机制。 每个对象都内含一个引用计数器 refcount,每个 reference 连接到对象,计数器加 1。当 ...
如果引用计数归零,表明没有其他变量指向该值,垃圾回收机制就会释放内存。 3. **引用(REFERENCE)**:在 PHP7 中,引用不再只是一个标记位,而是一个独立的数据结构 `_zend_reference`。这个结构包含了垃圾收集...