深入解析PHP的引用计数机制

PHP的变量声明并赋值后,变量名存在符号表中,而值和类信息存在zval中,zval中包含四个变量,is_ref,refcount,value,type,zval源码如下


代码如下:

struct _zval_struct { 
    /* Variable information */ 
    zvalue_value value;     /* value */ 
    zend_uint refcount__gc; 
    zend_uchar type;    /* active type */ 
    zend_uchar is_ref__gc; 
};

refcount表示value地址与其相同的zval共有多少个,refcount=0时,zval被销毁
is_ref表示一个zval是否被引用,有“0”和“1”两种状态

此处分析一下什么时候zval会被复制或者开辟新的内存空间呢
1.当is_ref=0,且refcount>1时,如果改变某个指向该zval的变量的值,会生成新zval,原zval的refcount--,例如:$a=1;$b=$a;$b=2;,zval将被复制,也就是说原先ab指向同一个zval,后来b会使用新开辟的zval

2.当is_ref=0,且refcount>1时,如果将zval赋值给某个引用变量,那么用来赋值和变量和被赋值的变量会使用同一个原zval,而其他指向原zval的变量将会指向一个新复制的zval,且refcount会被重新计算,例如:$a=1;$b=$a;$c=$a;$d=&$a;,此时ad使用原zval,bc使用新复制出来的zval

3.当is_ref=1,且refcount>1时,如果将zval复制给某个非引用变量,该非引用变量会使用一个新复制的zval,元zval的refcount不变,例如:$a=1;$b=&$a;$c=$a,那么ab使用原zval,而c使用新复制的zval
type表示该zval的值类型,宏定义如下


代码如下:

#define IS_NULL     0 
#define IS_LONG     1 
#define IS_DOUBLE   2 
#define IS_BOOL     3 
#define IS_ARRAY    4 
#define IS_OBJECT   5 
#define IS_STRING   6 
#define IS_RESOURCE 7 
#define IS_CONSTANT 8 
#define IS_CONSTANT_ARRAY   9

value表示该zval的值,他也是个共同体,代码如下


代码如下:

typedef union _zvalue_value { 
    long lval;                  /* long value */ 
    double dval;                /* double value */ 
    struct { 
        char *val; 
        int len; 
    } str; 
    HashTable *ht;              /* hash table value */ 
    zend_object_value obj; 
} zvalue_value;

现在你知道php是如何类型变换的了,因为他的值存的其实是个可以代表任何类型的结构体,而具体的取值则根据type来决定是用共同体里的哪个变量来存值的

见下面的例子1


代码如下:

.-----------
$a = 1;
$b = $a;
$c = $a;
.-----------
$d = &$a;
.-----------
$a = 2;
.-----------
$b = null;

查看refcount,is_ref,zval的变化
执行完第一部分后来看看输出
1-----------------------------
a:(refcount=3, is_ref=0),int 1
b:(refcount=3, is_ref=0),int 1
c:(refcount=3, is_ref=0),int 1
可以看出来a,b,c使用同一个zval
再看执行完第二部分的
2----------------------------
a:(refcount=2, is_ref=1),int 1
b:(refcount=2, is_ref=0),int 1
c:(refcount=2, is_ref=0),int 1
d:(refcount=2, is_ref=1),int 1
注意此时a,d在一起了,他们使用同一个zval,而bc使用一个新生成的zval,同时重新计算两个zval的refcount和is_ref
3----------------------------
a:(refcount=2, is_ref=1),int 2
b:(refcount=2, is_ref=0),int 1
c:(refcount=2, is_ref=0),int 1
d:(refcount=2, is_ref=1),int 2
可以知道ad这两个is_ref=1的好基友的值是同时改变的
4----------------------------
a:(refcount=2, is_ref=1),int 2
b:(refcount=1, is_ref=0),null
c:(refcount=1, is_ref=0),int 1
d:(refcount=2, is_ref=1),int 2
bc由于他们的zval的is_ref=0,所以他们不是好基友,他们的值不会同时改变,于是bc的zval再次分裂,b = null c = 1

(0)

相关推荐

  • PHP中的reflection反射机制测试例子

    Java类反射应用得非常广泛几乎是所有框架的最核心部分,PHP程序员似乎从不关心反射.尝试着用java的思想去理解php的反射,跟java基本上基本一致.参考了php手册:http://www.php.net/manual/zh/book.reflection.php. ReflectTest.php: <?php   class ReflectTest {       /**      * 用户ID      */     private $userId;       /**      * 用

  • 析构函数与php的垃圾回收机制详解

    析构函数:当某个对象成为垃圾或者当对象被显式销毁时执行. GC(Garbage Collector) 在PHP中,没有任何变量指向这个对象时,这个对象就成为垃圾.PHP会将其在内存中销毁. 这是PHP的GC(Garbage Collector)垃圾处理机制,防止内存溢出. 当一个PHP线程结束时,当前占用的所有内存空间都会被销毁,当前程序中的所有对象同样被销毁. __destruct() 析构函数 __destruct() 析构函数,是在垃圾对象被回收时执行. 析构函数也可以被显式调用,但不要这

  • php中Session的生成机制、回收机制和存储机制探究

    1.php中session的生成机制 我们先来分析一下PHP中是怎么生成一个session的.设计出session的目的是保持每一个用户的各种状态来弥补HTTP协议的不足(无状态).我们现在有一个疑问,我们都知道session是保存在服务器的,既然它用于保持每一个用户的状态那它利用什么来区别用户的呢?这个时候就得借助cookie了.当我们在代码中调用session_start();时,PHP会同时往SESSION的存放目录(默认为/tmp/)和客户端的cookie目录各生成一个文件.sessio

  • PHP 反射机制实现动态代理的代码

    演示用代码如下所示:  复制代码 代码如下: <?php class ClassOne { function callClassOne() { print "In Class One"; } } class ClassOneDelegator { private $targets; function __construct() { $this->target[] = new ClassOne(); } function __call($name, $args) { fore

  • PHP Session机制简介及用法

    当服务器创建了一个session(session_start()),服务器将会在服务器的指定文件夹下创建一个session文件,其名称为sessionID,并当做cookie的值发送给浏览器.浏览器每次访问该服务器时便会带着这个cookie,服务器便会识别改sessionID,找到相应的session文件.该文件中存放了若干键值对.该session文件所存放的文件夹可以在配置文件php,ini中修改. cookie 每次请求页面的时候进行验证,如果用户信息存储在数据库中,每次都要执行一次数据库查

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

    如果你安装了xdebug,就可以用xdebug_debug_zval()显示"zval"的信息了.如下: 复制代码 代码如下: <?php$str = "jb51.net";xdebug_debug_zval('str'); 结果: str:(refcount=1, is_ref=0),string 'jb51.net' (length=10) 只有当变量容器在"refcount"变成0时就被销毁.当你unset()一个变量时,想要的&qu

  • PHP OPP机制和模式简介(抽象类、接口和契约式编程)

    1.抽象类 抽象类机制中总是要定义一个公共的基类,而将特定的细节留给继承者来实现.通过抽象概念,可以在开发项目中创建扩展性很好的架构.任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的.被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现.在类的声明中使用 abstract 修饰符就可以将某个类声明为抽象的. 1.1方法原型(prototype) 是指方法的定义中剔除了方法体之后的签名.它包括存取级别.函数关键字.函数名称和参数.他不包含({

  • 实例介绍PHP的Reflection反射机制

    PHP5添加了一项新的功能:Reflection.这个功能使得程序员可以reverse-engineer class, interface,function,method and extension.通过PHP代码,就可以得到某object的所有信息,并且可以和它交互. 假设有一个类Person: 复制代码 代码如下: class Person {   /**      * For the sake of demonstration, we"re setting this private    

  • PHP反射机制用法实例

    本文实例讲述了PHP反射机制的用法,分享给大家供大家参考之用.具体方法如下: 演示示例代码如下所示: <?php class ClassOne { function callClassOne() { print "In Class One"; } } class ClassOneDelegator { private $targets; function __construct() { $this->target[] = new ClassOne(); } function

  • 深入解析PHP的引用计数机制

    PHP的变量声明并赋值后,变量名存在符号表中,而值和类信息存在zval中,zval中包含四个变量,is_ref,refcount,value,type,zval源码如下 复制代码 代码如下: struct _zval_struct {      /* Variable information */      zvalue_value value;     /* value */      zend_uint refcount__gc;      zend_uchar type;    /* ac

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

    最近在查阅 PHP7 垃圾回收的资料的时候,网上的一些代码示例在本地环境下运行时出现了不同的结果,使我一度非常迷惑. 仔细一想不难发现问题所在:这些文章大多是 PHP5.x 时代的,而 PHP7 发布后,采用了新的 zval 结构,相关的资料也比较贫瘠,所以我结合一些资料做了一个总结, 主要侧重于解释新 zval 容器中的引用计数机制 ,如有谬误,还望不吝指教. PHP7 中新的 zval 结构 明人不说暗话,先看代码! struct _zval_struct { union { zend_lo

  • Python引用计数操作示例

    本文实例讲述了Python引用计数操作.分享给大家供大家参考,具体如下: 为了简化内存管理,Python通过引用计数机制实现了自动的垃圾回收功能,Python中的每个对象都有一个引用计数,用来计数该对象在不同场所分别被引用了多少次.每当引用一次Python对象,相应的引用计数就增1,每当消毁一次Python对象,则相应的引用就减1,只有当引用计数为零时,才真正从内存中删除Python对象. import ctypes def get_ref(obj): """ returns

  • JavaScript垃圾回收机制(引用计数,标记清除,性能优化)

    目录 一.前言 二.何为垃圾 三.垃圾回收 四.可达性(Reachability) 五.可达性举例 层次关联 相互关联 可达孤岛 六.垃圾回收算法 引用计数 标记清除 七.性能优化 分代回收 增量收集 空闲收集 八.总结 一.前言 垃圾回收是JavaScript的隐藏机制,我们通常无需为垃圾回收劳心费力,只需要专注功能的开发就好了.但是这并不意味着我们在编写JavaScript的时候就可以高枕无忧了,伴随着我们实现的功能越来越复杂,代码量越积越大,性能问题就变的越来越突出.如何写出执行速度更快,

  • C++浅拷贝与深拷贝及引用计数分析

    C++浅拷贝与深拷贝及引用计数分析 在C++开发中,经常遇到的一个问题就是与指针相关的内存管理问题,稍有不慎,就会造成内存泄露.内存破坏等严重的问题.不像Java一样,没有指针这个概念,所以也就不必担心与指针相关的一系列问题,但C++不同,从C语言沿袭下来的指针是其一大特点,我们常常要使用new/delete来动态管理内存,那么问题来了,特别是伴随着C++的继承机制,如野指针.无效指针使用.内存泄露.double free.堆碎片等等,这些问题就像地雷一样,一不小心就会踩那么几颗. 先来谈一下C

  • Swift源码解析之弱引用

    序言: 各个社区有关 Objective-C weak 机制的实现分析文章有很多,然而 Swift 发布这么长时间以来,有关 ABI 的分析文章一直非常少,似乎也是很多 iOS 开发者未涉及的领域- 本文就从源码层面分析一下 Swift 是如何实现 weak 机制的. 下面话不多说了,来一起看看详细的介绍吧 准备工作 由于 Swift 源码量较大,强烈建议大家把 repo clone 下来,结合源码一起来看这篇文章. $ git clone https://github.com/apple/sw

  • 详解PHP的引用计数

    什么是引用计数 在PHP的数据结构中,引用计数就是指每一个变量,除了保存了它们的类型和值之外,还额外保存了两个内容,一个是当前这个变量是否被引用,另一个是引用的次数.为什么要多保存这样两个内容呢?当然是为了垃圾回收(GC).也就是说,当引用次数为0的时候,这个变量就没有再被使用了,就可以通过 GC 来进行回收,释放占用的内存资源.任何程序都不能无限制的一直占用着内存资源,过大的内存占用往往会带来一个严重的问题,那就是内存泄露,而 GC 就是PHP底层自动帮我们完成了内存的销毁,而不用像 C 一样

  • 详谈swift内存管理中的引用计数

    在swift中,每一个对象都有生命周期,当生命周期结束会调用deinit()函数进行释放内存空间. 观察这一段代码: class Person{ var name: String var pet: Pet? init(name: String){ self.name = name print("Person", name, "is initialized") } init(name: String, petName: String){ self.name = nam

  • Swift编程中用以管理内存的自动引用计数详解

    Swift 内存管理功能是通过使用自动引用计数(ARC)来处理.ARC用于初始化和取消初始化所述系统资源,从而释放使用的类实例的存储器空间当实例不再需要.ARC跟踪代码的实例有效地管理存储资源之间的关系的信息. ARC的功能 在每一次一个新的类实例被创建时ARC分配一块内存以存储信息 init() 关于实例类型和其值的信息存储在存储器中 当类实例不再需要它自动由 deinit() 释放,用于进一步类实例的存储和检索的存储空间 ARC保存在磁道当前参照类实例的属性,常量和变量,使得 deinit(

  • 实例讲解Swift中引用类型的ARC自动引用计数

    一.引言 ARC(自动引用计数)是Objective-C和Swift中用于解决内存管理问题的方案.在学习Objective-C编程时经常会学习到一个关于ARC的例子:在一个公用的图书馆中,每次进入一人就将卡插入,走的时候将自己的卡拔出拿走.图书馆系统会判定只要有卡插入,就将图书馆的灯打开,当所有卡都被取走后,将图书馆的灯关掉.这个例子对应于Objective-C中的对象声明周期管理十分贴切.每当一个对象增加一个引用时,其引用计数会加1,当一个引用被取消时,对象的引用计数减1,当引用计数减为0时,

随机推荐