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)“编码”可以理 ...
随机推荐
- [Leetcode][动态规划] 第935题 骑士拨号器
一.题目描述 国际象棋中的骑士可以按下图所示进行移动: 我们将 “骑士” 放在电话拨号盘的任意数字键(如上图所示)上,接下来,骑士将会跳 N-1 步 ...
- Selenium+python上传本地文件或者图片
基于input标签的,有属性type = file: 首先定位到点击上传的元素(input)然后直接使用send_keys()将文件在本地的路径传进去 代码如下(例子来源于本页面上传图片和文件): f ...
- Linux 安装CentOS7操作系统
作者:小啊博 QQ:762641008 转载请声明:https://www.cnblogs.com/-bobo 1.安装操作系统 开启虚拟机后会出现以下界面 Install CentOS 7 安装Ce ...
- SpringBoot数据聚合(spring-boot-data-aggregator-starter)
背景 接口开发是后端开发中最常见的场景, 可能是RESTFul接口, 也可能是RPC接口. 接口开发往往是从各处捞出数据, 然后组装成结果, 特别是那些偏业务的接口. 例如, 我现在需要实现一个接口, ...
- java8 函数接口
[前言] java8新特性 java8 Optional使用总结 java8 lambda表达式 Java 8 时间日期使用 1.函数式接口新特性 java8中引入了函数式接口新特性,使用@Funct ...
- Idea插件之IdeTalk
前言 随着越来越多的公司与Java工程师,逐步从Eclipse过度到Idea,安装相应的插件可能会成倍的增加工作效率. IDETalk是由JetBrains的工程师开发的一款代码级的协同工具,主要是为 ...
- touch,stat
touch(选项)(参数) 一是可以用来创建空文件,二是用来改变文件的元属性-a:修改文件的访问时间为当前时间-m:修改文件的改变时间为当前时间-r:把文件的属性修改成和某些文件一样的时间-t:修改成 ...
- raspbian修改swap分区为硬盘上的分区
一直以为raspbian的swap分区和平常装系统时候的swap分区是一样的,最近用u盘插在树莓派上运行后发现这个交换分区和我想象中不一样. 百度后发现它是一个文件挂上去的.直接搞把! pi@rasp ...
- 理解Android中的注解与反射
反射 Java反射(Reflection)定义 Java反射机制是指在运行状态中 对于任意一个类,都能知道这个类的所有属性和方法:对于任何一个对象,都能够调用它的任何一个方法和属性: 这样动态获取新的 ...
- ASRWGAN: Wasserstein Generative Adversarial Network for Audio Super Resolution
ASEGAN:WGAN音频超分辨率 这篇文章并不具有权威性,因为没有发表,说不定是外国的某个大学的毕业设计,或者课程结束后的作业.或者实验报告. CS230: Deep Learning, Sprin ...