变量结构

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. 主动降噪技术(ANC)的前生今世--概念历史

    一 概念 假如使用一句通俗的语言来概述ANC的原理的话,那就是:通过发出与噪声相位相反,频率.振幅相同的声波与噪声干涉实现相位抵消. 使用比较正式的语言来解释就是:动降噪通过降噪系统产生与外界噪音相等 ...

  2. Java面向对象笔记(五分钟秒懂)

    面向对象概念 面向对象三大特征:封装,继承,多态 面向对象编程(OOP,Object Oriented Programing)是相对于面向过程编程说的,之前写的代码基本都是纯的面向过程编程的,当项目复 ...

  3. CDH高可用hadoop集群性能配置

    1.HDFS的高可用配置 dfs.namenode.edits.dir (NameNode 编辑目录) : 写入 NameNode 编辑的本地文件系统上的目录.未指定将存放在namenode数据目录中 ...

  4. 利用Python进行数据分析:【NumPy】

    一.NumPy:数组计算1.NumPy是高性能科学计算和数据分析的基础包.它是pandas等其他各种工具的基础.2.NumPy的主要功能: # ndarray,一个多维数组结构,高效且节省空间 # 无 ...

  5. Creating a Store Locator with PHP, MySQL & Google Maps(保存地图坐标 经纬度方法 google mysql)

    Google Geo APIs Team August 2009 This tutorial is intended for developers who are familiar with PHP/ ...

  6. Java 爬虫遇到需要登录的网站,该怎么办?

    这是 Java 网络爬虫系列博文的第二篇,在上一篇 Java 网络爬虫,就是这么的简单 中,我们简单的学习了一下如何利用 Java 进行网络爬虫.在这一篇中我们将简单的聊一聊在网络爬虫时,遇到需要登录 ...

  7. ElasticSearch实战系列三: ElasticSearch的JAVA API使用教程

    前言 在上一篇中介绍了ElasticSearch实战系列二: ElasticSearch的DSL语句使用教程---图文详解,本篇文章就来讲解下 ElasticSearch 6.x官方Java API的 ...

  8. Docker5-docker私库的搭建及常用方法-harbor-registry方式

    一.简介 1.官方已经提供registry镜像为什么还需要用harbor 1)registry缺少镜像清理机制,可以push但是不能删除,耗费空间 2)registry缺乏相应的扩展机制 3)harb ...

  9. spring5 源码深度解析----- @Transactional注解的声明式事物介绍(100%理解事务)

    面的几个章节已经分析了spring基于@AspectJ的源码,那么接下来我们分析一下Aop的另一个重要功能,事物管理. 事务的介绍 1.数据库事物特性 原子性多个数据库操作是不可分割的,只有所有的操作 ...

  10. [开源]如何使用goapp写你的后台管理系统_golang

    简析 基于 Gin + GORM + Casbin + vue-element-admin 实现的权限管理系统. 基于Casbin 实现RBAC权限管理. 前端实现: vue-element-admi ...