变量结构

zval 结构体

PHP 所有类型的变量在底层都会以 zval 结构体的形式实现 (源码文件Zend/zend.h)

源码根目录搜索

grep -rin --color --include=*.h --include=*.c _zval_struct *

struct _zval_struct {
/* Variable information */
zvalue_value value; /* 变量value值 */
zend_uint refcount__gc; /* 引用计数内存中使用次数,为0删除该变量 */
zend_uchar type; /* 变量类型 */
zend_uchar is_ref__gc; /* 区分是否是引用变量,是引用为1,否则为0 */
};

注:上面zval结构体是 php5.3 版本之后的结构,php5.3 之前因为没有引入新的垃圾回收机制,即 GC,所以命名也没有_gc;而 php7 版本之后由于性能问题所以改写了 zval 结构,这里不再表述

zval 组成

上面结构体内容可以看出每一个 PHP 变量都会由 变量类型value值引用计数次数是否是引用变量 四部分组成

  • type 变量类型

    type 的值为以下常量:
    IS_NULL, IS_BOOL, IS_LONG, IS_DOUBLE, IS_STRING, IS_ARRAY, IS_OBJECT, IS_RESOURCE

  • value

    因为要存储多种类型,所以 value 是一个 union,也由此实现了弱类型

    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;
  • refcount__gc 引用计数次数

  • is_ref__gc 是否是引用变量

变量类型

看到这里,可能会有小伙伴们问我,PHP 不是有 8 种数据类型吗?但是为什么对应的 zval 的 value 值只有 5 种?

原因是这样的,PHP 出于对内存节省的考虑,所以对于一些变量类型做了复用,并没有一一对应去定义每个变量类型

下面我们看一下 zval 的每个 value 值所对应的变量类型

zval.value.lval => 整型、布尔型、资源
zval.value.dval => 浮点型
zval.value.str => 字符串
zval.value.*ht => 数组
zval.value.obj => 对象

看到这里大家可能会比较奇怪,「布尔型」和「资源」是怎么对应到 zval.value 的 lval 上的呢?还有,NULL呢?

布尔型
就像我们会将 true 和 false 映射成 0 和 1 进行数据库存储一样,PHP 也是这么做的。所以 PHP 发现 zval 的type 值是「布尔型」时,会将「布尔型」转成 0 或 1 存储在 zval.value 的 lval 中

zval.type = IS_BOOL
zval.value.lval = 1/0

资源
「资源」对于 PHP 来说属于一个比较特殊的变量,而 PHP 会将每个「资源」对应的「资源标识编号」存储在 zval.value 的 lval 中。常见的资源有:文件句柄、数据库句柄等

zval.type = IS_RESOURCE
zval.value.lval = 资源标识编号

NULL
对于 NULL 来说,就更好理解了,因为本身通过 zval 的 type 值即可区分,所以并没有将 NULL 值存储在 zval 的 value 中

zval.type = IS_NULL

变量生成

PHP 作为一门动态语言,没有先声明变量后赋值的习惯,所以都是拿来一个常量变量直接就进行了赋值,那么是如何实现的呢?

举例:

$name = "new string";

变量容器生成

其实每次变量被常量赋值时,都会对应生成一个变量容器。刚才的例子会生成一个变量容器,容器的 type 是字符串类型,而 value 值则是『new string』,且此时该变量容器的 ref_count 会加 1

变量名和变量容器关联

而变量 name 是如何与变量容器关联起来的呢?其实也是使用了 PHP 的一个内部机制,即 哈希表。每个变量的变量名 和指向 zval 结构的 指针存储 在 哈希表 内,以此实现了变量名到变量容器的映射。

以变量被赋值为常量为例

当变量被重新引用后,指向 zval 结构体的指针也就发生了变化,而之前指向的 zval 结构体由于没有被引用了,所以 refcount 清零。

变量作用域

上面我们提到了「变量名」和「变量容器」映射的概念。对于 PHP 来说,变量有 全局变量局部变量 之分;那么,他们都是存储到一个 哈希表 内了么?

其实不是的,变量存储也有作用域的概念。

全局变量 被存储到了 全局符号表 内,而 局部变量 也就是指函数或对象内的变量,则被存储到了 活动符号表 内(每个函数或对象都单独维护了自己的活动符号表。活动符号表的生命周期,从函数或对象被调用时开始,到调用完成时结束)

变量销毁

变量销毁,分为以下几种情况:
1、手动销毁
2、垃圾回收机制销毁(引用计数清0销毁和根缓冲区满后销毁)

我们这次主要讲一下手动销毁,即 unset,每次销毁时都会将符号表内的 变量名 和它指向的 **变量容器 zval ** 进行销毁,并将对应的内存归还到 PHP 所维护的内存池内(按内存大小划分到对应内存列表中)

而对于垃圾回收机制的销毁,请看下篇文章php5底层原理之垃圾回收机制

参考资料:

PHP底层原理分析(一):PHP变量的底层实现

php底层原理之变量(一)

PHP5底层原理之变量的更多相关文章

  1. PHP5底层原理之垃圾回收机制

    概念 垃圾回收机制 是一种内存动态分配的方案,它会自动释放程序不再使用的已分配的内存块. 垃圾回收机制 可以让程序员不必过分关心程序内存分配,从而将更多的精力投入到业务逻辑. 与之相关的一个概念,内存 ...

  2. Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL

    Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL 1.1. $ 美元字符, php 黑头1 1.2. 默认变量的范围和声明:1 1.3 ...

  3. Servlet底层原理、Servlet实现方式、Servlet生命周期

    Servlet简介 Servlet定义 Servlet是一个Java应用程序,运行在服务器端,用来处理客户端请求并作出响应的程序. Servlet的特点 (1)Servlet对像,由Servlet容器 ...

  4. 深入源码分析SpringMVC底层原理(二)

    原文链接:深入源码分析SpringMVC底层原理(二) 文章目录 深入分析SpringMVC请求处理过程 1. DispatcherServlet处理请求 1.1 寻找Handler 1.2 没有找到 ...

  5. 并发之volatile底层原理

    15.深入分析Volatile的实现原理 14.java多线程编程底层原理剖析以及volatile原理 13.Java中Volatile底层原理与应用 12.Java多线程-java.util.con ...

  6. 【Servlet】(1)Servlet简介、Servlet底层原理、Servlet实现方式、Servlet生命周期

    一.Servlet简介 1.Servlet定义: Servlet(Server Applet)是Java Servlet的简称,是为小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交 ...

  7. MVC底层原理

    窥探ASP.Net MVC底层原理 实现跨越Session的分布式TempData 1.问题的引出 我相信大家在项目中都使用过TempData,TempData是一个字典集合,一般用于两个请求之间临时 ...

  8. iOS底层原理总结 - 探寻block的本质(一)

        面试题 block的原理是怎样的?本质是什么? __block的作用是什么?有什么使用注意点? block的属性修饰词为什么是copy?使用block有哪些使用注意? block在修改NSMu ...

  9. KVC与Runtime结合使用(案例)及其底层原理

    一.KVC 的用法和实践 用法 KVC(Key-value coding)键值编码,顾名思义.额,简单来说,是可以通过对象属性名称(Key)直接给属性值(value)编码(coding)“编码”可以理 ...

随机推荐

  1. 提交任务到spark(以wordcount为例)

    1.首先需要搭建好hadoop+spark环境,并保证服务正常.本文以wordcount为例. 2.创建源文件,即输入源.hello.txt文件,内容如下: tom jerry henry jim s ...

  2. WordPress对接微信小程序遇到的问题

    1.文章内容中的“<”和“>”字符显示问题 小程序是使用“wxPares工具来实现html转wxml的,如果你的文本包含了代码比如xml会携带<>符号,程序会将其转化,造成解析 ...

  3. JSP自定义标签的使用简化版

    在jsp中 如果不想页面中出现java代码 这个时候就需要使用到jsp的自定义标签库技术了 自定义标签库 能够有效的减少jsp中java代码的出现 使其更加自然的像html页面一样 如果要使用jsp自 ...

  4. ASP.NET Core 3.0 WebApi中使用Swagger生成API文档简介

    参考地址,官网:https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/getting-started-with-swashbuckle?view ...

  5. [python]兔子问题,斐波那契数列 递归&非递归

    假设一对幼年兔子需要一个月长成成年兔子,一对成年兔子一个月后每个月都可以繁衍出一对新的幼年兔子(即兔子诞生两个月后开始繁殖).不考虑死亡的情况,问第 N 个月时共有多少对兔子? 结果前几个月的兔子数量 ...

  6. Spring boot集成Rabbit MQ使用初体验

    Spring boot集成Rabbit MQ使用初体验 1.rabbit mq基本特性 首先介绍一下rabbitMQ的几个特性 Asynchronous Messaging Supports mult ...

  7. Dubbo学习系列之十三(Mycat数据库代理)

    软件界有只猫,不用我说,各位看官肯定知道是哪只,那就是大名鼎鼎的Tomcat,现在又来了一只猫,据说是位东方萌妹子,暂且认作Tom猫的表妹,本来叫OpencloudDB,后又改名为Mycat,或许Ca ...

  8. SpringBootSecurity学习(05)网页版登录内存中配置默认用户

    默认用户 前面的例子中我们使用的都是配置文件中配置好的默认用户: 除了可以配置账号密码,还可以在配置文件中配置角色: 这个角色是后面实现权限过滤的重要内容,后面会重点讨论. 在内存中配置默认用户 这样 ...

  9. mysql 事物四大特性和事物的四个隔离

    1.事物四大特性(ACID) 原子性(atomicity):一个事务必须视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部 ...

  10. Linux——基本命令

    目录 一.目录切换命令 二.目录操作命令(增删改查) 2.1增加目录 2.2查看目录 2.3寻找目录(搜索) 2.4修改目录名称 2.5移动目录位置(剪切) 2.6拷贝目录 2.7删除目录 三.文件的 ...