`
student_lp
  • 浏览: 429452 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论
阅读更多

一、引用计数基本知识

    每个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清楚之前,将耗费不少空间内存。如果你要实现分析算法,或者要做其他像一个子元素指向它的父元素这种的事情,这种情况就会经常发生。当然,同样的情况也会发生在对象上,实际上对象更可能出现这种情况,因为对象总是隐式的被引用。

    如果上面的情况仅仅发生一两次倒也没有什么,但是如果出现几千次或更多,这显然是个大问题。在长时间运行的脚本,例如请求基本上不会结束的守护进程或者单元测试中的大的套件中,就有可能消耗掉大量的内存。

  • 大小: 9.3 KB
  • 大小: 10.1 KB
  • 大小: 5.9 KB
  • 大小: 5.4 KB
分享到:
评论

相关推荐

    PHP垃圾回收机制简单说明

    PHP使用了引用计数(reference counting)这种单纯的垃圾回收(garbage collection)机制。每个对象都内含一个引用计数器,每个reference连接到对象,计数器加1。当reference离开生存空间或被设为NULL,计数器减1。当...

    PHP进阶学习之垃圾回收机制详解

    本文实例讲述了PHP垃圾回收机制。分享给大家供大家参考,具体如下: 一、概念 垃圾回收机制是一种动态存储分配的方案。它会自动释放程序不再需要的已分配的内存块。垃圾回收机制可以让程序员不必过分关心程序内存...

    掌握PHP垃圾回收机制详解

    本文主要和大家分享掌握php垃圾回收机制的知识,希望能帮助到大家。 引用计数基本知识 官网的解答如下 每个php变量存在一个叫”zval”的变量容器中一个zval变量容器,除了包含变量的类型和值 ,还包括两个字节的额外...

    腾讯PHP面试题_腾讯php面试题_

    php 的垃圾回收机制 PHP 可以自动进行内存管理,清除不需要的对象。 PHP 使用了引用计数 (reference counting) GC 机制。 每个对象都内含一个引用计数器 refcount,每个 reference 连接到对象,计数器加 1。当 ...

    PHP垃圾回收机制引用计数器概念分析

    如果你安装了xdebug,就可以用xdebug_debug_zval()显示...当你unset()一个变量时,想要的“zval”中refcount就会减1,再来说说前几天遇到的unset引用问题: 复制代码 代码如下:<?php$a = “aaa”;$b = & $a;unset

    深入理解 PHP7 中全新的 zval 容器和引用计数机制

    最近在查阅 PHP7 垃圾回收的资料的时候,网上的一些代码示例在本地环境下运行时出现了不同的结果,使我一度非常迷惑。 仔细一想不难发现问题所在:这些文章大多是 PHP5.x 时代的,而 PHP7 发布后,采用了新的 zval ...

    php引用计数器进行垃圾收集机制介绍

    每一种计算机语言都有自己的自动垃圾回收机制,让程序员不必过分关心程序内存分配,php也不例外,但是在面向对象编程(OOP)编程中,有些对象需要显式的销毁;防止程序执行内存溢出

    PHP:世界上最好的语言:rocket:

    php 5.3之前使用的垃圾回收机制是单纯的"引用计数": 也就是每个内存对象都分配一个计数器,当内存对象被变量引用时,计数器+1; 当变量引用撤掉后,计数器-1;当计数器=0时,表明内存对象没有被使用, 该内存对象则...

    php-src-analysis:PHP源码分析注解

    内存申请与释放流程,垃圾回收; 持久化 第四章线程安全 我是否该启用ZTS?各有什么优缺点? 如何打造一个ZTS的程序/扩展? 第五章变量与常量PHP中的数据类型。 变量,常量与静态变量。 引用计数机制; 资源的创造与...

Global site tag (gtag.js) - Google Analytics