PHP5底层原理之变量
变量结构
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_RESOURCEvalue 值
因为要存储多种类型,所以 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底层原理之垃圾回收机制
参考资料:
PHP5底层原理之变量的更多相关文章
- PHP5底层原理之垃圾回收机制
概念 垃圾回收机制 是一种内存动态分配的方案,它会自动释放程序不再使用的已分配的内存块. 垃圾回收机制 可以让程序员不必过分关心程序内存分配,从而将更多的精力投入到业务逻辑. 与之相关的一个概念,内存 ...
- Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL
Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL 1.1. $ 美元字符, php 黑头1 1.2. 默认变量的范围和声明:1 1.3 ...
- Servlet底层原理、Servlet实现方式、Servlet生命周期
Servlet简介 Servlet定义 Servlet是一个Java应用程序,运行在服务器端,用来处理客户端请求并作出响应的程序. Servlet的特点 (1)Servlet对像,由Servlet容器 ...
- 深入源码分析SpringMVC底层原理(二)
原文链接:深入源码分析SpringMVC底层原理(二) 文章目录 深入分析SpringMVC请求处理过程 1. DispatcherServlet处理请求 1.1 寻找Handler 1.2 没有找到 ...
- 并发之volatile底层原理
15.深入分析Volatile的实现原理 14.java多线程编程底层原理剖析以及volatile原理 13.Java中Volatile底层原理与应用 12.Java多线程-java.util.con ...
- 【Servlet】(1)Servlet简介、Servlet底层原理、Servlet实现方式、Servlet生命周期
一.Servlet简介 1.Servlet定义: Servlet(Server Applet)是Java Servlet的简称,是为小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交 ...
- MVC底层原理
窥探ASP.Net MVC底层原理 实现跨越Session的分布式TempData 1.问题的引出 我相信大家在项目中都使用过TempData,TempData是一个字典集合,一般用于两个请求之间临时 ...
- iOS底层原理总结 - 探寻block的本质(一)
面试题 block的原理是怎样的?本质是什么? __block的作用是什么?有什么使用注意点? block的属性修饰词为什么是copy?使用block有哪些使用注意? block在修改NSMu ...
- KVC与Runtime结合使用(案例)及其底层原理
一.KVC 的用法和实践 用法 KVC(Key-value coding)键值编码,顾名思义.额,简单来说,是可以通过对象属性名称(Key)直接给属性值(value)编码(coding)“编码”可以理 ...
随机推荐
- 主动降噪技术(ANC)的前生今世--概念历史
一 概念 假如使用一句通俗的语言来概述ANC的原理的话,那就是:通过发出与噪声相位相反,频率.振幅相同的声波与噪声干涉实现相位抵消. 使用比较正式的语言来解释就是:动降噪通过降噪系统产生与外界噪音相等 ...
- Java面向对象笔记(五分钟秒懂)
面向对象概念 面向对象三大特征:封装,继承,多态 面向对象编程(OOP,Object Oriented Programing)是相对于面向过程编程说的,之前写的代码基本都是纯的面向过程编程的,当项目复 ...
- CDH高可用hadoop集群性能配置
1.HDFS的高可用配置 dfs.namenode.edits.dir (NameNode 编辑目录) : 写入 NameNode 编辑的本地文件系统上的目录.未指定将存放在namenode数据目录中 ...
- 利用Python进行数据分析:【NumPy】
一.NumPy:数组计算1.NumPy是高性能科学计算和数据分析的基础包.它是pandas等其他各种工具的基础.2.NumPy的主要功能: # ndarray,一个多维数组结构,高效且节省空间 # 无 ...
- 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/ ...
- Java 爬虫遇到需要登录的网站,该怎么办?
这是 Java 网络爬虫系列博文的第二篇,在上一篇 Java 网络爬虫,就是这么的简单 中,我们简单的学习了一下如何利用 Java 进行网络爬虫.在这一篇中我们将简单的聊一聊在网络爬虫时,遇到需要登录 ...
- ElasticSearch实战系列三: ElasticSearch的JAVA API使用教程
前言 在上一篇中介绍了ElasticSearch实战系列二: ElasticSearch的DSL语句使用教程---图文详解,本篇文章就来讲解下 ElasticSearch 6.x官方Java API的 ...
- Docker5-docker私库的搭建及常用方法-harbor-registry方式
一.简介 1.官方已经提供registry镜像为什么还需要用harbor 1)registry缺少镜像清理机制,可以push但是不能删除,耗费空间 2)registry缺乏相应的扩展机制 3)harb ...
- spring5 源码深度解析----- @Transactional注解的声明式事物介绍(100%理解事务)
面的几个章节已经分析了spring基于@AspectJ的源码,那么接下来我们分析一下Aop的另一个重要功能,事物管理. 事务的介绍 1.数据库事物特性 原子性多个数据库操作是不可分割的,只有所有的操作 ...
- [开源]如何使用goapp写你的后台管理系统_golang
简析 基于 Gin + GORM + Casbin + vue-element-admin 实现的权限管理系统. 基于Casbin 实现RBAC权限管理. 前端实现: vue-element-admi ...