PHP内核的存储机制(分离/改变)
或许你知道,或许你不知道,PHP是一个弱类型,动态的脚本语言。所谓弱类型,就是说PHP并不严格验证变量类型(严格来讲,PHP是一个中强类型语言),在申明一个变量的时候,并不需要显示指明它保存的数据的类型。比如:$a = 1; (整形) $a ="1";(字符串)
一直使用PHP,但它究竟什么,底层是怎么实现才成就了PHP这样方便快捷的弱类型语言。
最近也查阅了很多书籍,还有相关博客资料,了解到了许多关于PHP内核的一些机制。
php简单的理解就是一个c语言的类库,你去php.net 下面下载一下它的源代码就会发现,首先php的内核是zend engine ,它是一个用c语言写的函数库,用于处理底层的函数管理,内存管理,类管理,和变量管理。在内核上面,他们写了很多扩展,这些扩展大多数都是独立的。用操作系统来比喻的话,zend engine 就是一个操作系统,然后官方提供了很多“应用程序”,只是这个“应用程序” 不是media play 而是 mysql , libxml,dom。当然,你也可以根据zend engine 的api 开发自己的扩展。
下边开始介绍下PHP变量在内核中的存储机制。
1.zval结构
typedef struct _zval_struct zval;
typedef union _zvalue_value {long lval; /* long value */double dval; /* double value */struct {char *val; //4字节int len; //4字节} str;HashTable *ht; /* hash table value */zend_object_value obj;} zvalue_value;
struct _zval_struct {/* Variable information */zvalue_value value; /* 变量值保存在这里 12字节*/zend_uint refcount;//4字节,变量引用计数器zend_uchar type; /* active type变量类型 1字节*/zend_uchar is_ref;//是否变量被&引用,0表示非引用,1表示引用,1字节};
2.zend_uchar type
3.zend_uint refcount__gc
4.is_ref__gc
.
正如名字所示,ref_count__gc和is_ref__gc是PHP的GC机制所需的很重要的两个字段,这两个字段的值,可以通过xdebug等调试工具查看。
下面我们围绕zval,展开叙述,PHP变量到底是怎么个存储机制。
Xdebug的安装我在前边PHPstorm Xdebug调试也介绍过,这里不赘述,请看: phpstorm+Xdebug断点调试PHP
安装成功后,你的脚本中,可以通过xdebug_debug_zval打印Zval的信息,用法:
$var = 1;debug_zval_dump($var);$var_dup = $var;debug_zval_dump($var);
实例一:
$a = 1;$b = $a;$c = $b;$d = &$c; // 在一堆非引用赋值中,插入一个引用
整个过程图示如下:
---------------------------------------------------------
实例二:
$a = 1;$b = &$a;$c = &$b;$d = $c; // 在一堆引用赋值中,插入一个非引用
整个过程图示如下:
通过实例一、二,展现了,这就是PHP的copy on write写时分离机制、change on write写时改变机制
过程:
PHP在修改一个变量以前,会首先查看这个变量的refcount,如果refcount大于1,PHP就会执行一个分离的例程,
对于上面的实例一代码,当执行到第四行的时候,PHP发现$c指向的zval的refcount大于1,那么PHP就会复制一个新的zval出来,将原zval的refcount减1,并修改symbol_table,使得$a,$b和$c分离(Separation)。这个机制就是所谓的copy on write(写时复制/写时分离)。把$d指向的新zval的is_ref的值 == 1 ,这个机制叫做change on write(写时改变)
分离指的是:分离两个变量存储的zval的位置,让分开不指向同一个空间! (那如何判定是否要分离呢,依据是什么?见下边)
改变指的是,有&引用赋值时,要把新开辟的zval 的 is_ref 赋值为1
判定是否分离的条件:如果is_ref =1 或recount == 1,则不分离
if((*val)->is_ref || (*val)->refcount<2){//不执行Separation... ;//process}
---------------------------------------------------------------------------------------------------
实例三:(内存是如何泄漏的)
数组变量与普通变量生成的zval非常类似,但也有很大不同
$a = $array('one');
$a[] = &$a;
xdebug_debug_zval('a');
debug_zval_dump打印出zval的结构是:
a: (refcount=2, is_ref=1)=array (0 => (refcount=1, is_ref=0)='one', 1 => (refcount=2, is_ref=1)=...
)
上述输出中,…表示指向原始数组,因而这是一个循环的引用。如下图所示:
![](/assets/blank.gif)
现在,我们对$a执行unset操作,这会在symbol table中删除相应的symbol,同时,zval的refcount减1(之前为2),也就是说,现在的zval应该是这样的结构:
unset($a);
(refcount=1, is_ref=1)=array (0 => (refcount=1, is_ref=0)='one', 1 => (refcount=1, is_ref=1)=...
)
(应该ref_count=1)
这时,不幸的事情发生了!
Unset之后,虽然没有变量指向该zval,但是该zval却不能被GC(指PHP5.3之前的单纯引用计数机制的GC)清理掉,$a 被释放,但是$a里的$a[1]也指向了该zval,它没有被释放,导致zval的refcount均大于0。这样,这些zval实际上会一直存在内存中,直到请求结束(参考SAPI的生命周期)。在此之前,这些zval占据的内存不能被使用,便白白浪费了,换句话说,无法释放的内存导致了内存泄露。
如果这种内存泄露仅仅发生了一次或者少数几次,倒也还好,但如果是成千上万次的内存泄露,便是很大的问题了。尤其在长时间运行的脚本中(例如守护程序,一直在后台执行不会中断),由于无法回收内存,最终会导致系统“再无内存可用”,所以说,一定要避免这种操作。
垃圾回收机制:
例如:
$a = 1; $b = $a; unset($a);//$a开辟的内存不会回收
2.离开了变量的作用域后变量所占用的内存就会被自动清理(不包含静态变量,静态变量在脚本加载时创建,在脚本结束时释放),
本文参考文献:
- 鸟哥的深入变量引用/分离 http://www.laruence.com/2008/09/19/520.html
本文链接地址:http://blog.csdn.net/ty_hf/article/details/51057954
啊~终于写完了,清明三天假期也就这么过去了,今天4月4号,明天开始准备上班啦!~
PHP内核的存储机制(分离/改变)相关推荐
- 从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响(一)
从 2.4 到 2.6:Linux 内核可装载模 块机制的改变对设备驱动的影响 <?xml:namespace prefix = o ns = "urn:schemas-microso ...
- Linux内核的namespace机制分析
1. Linux内核namespace机制 Linux Namespaces机制提供一种资源隔离方案.PID,IPC,Network等系统资源不再是全局性的,而是属于某个特定的Namespace.每 ...
- linux 信号量锁 内核,Linux内核中锁机制之信号量、读写信号量
在上一篇博文中笔者分析了关于内存屏障.读写自旋锁以及顺序锁的相关内容,本篇博文将着重讨论有关信号量.读写信号量的内容. 六.信号量 关于信号量的内容,实际上它是与自旋锁类似的概念,只有得到信号量的进程 ...
- Broker存储机制详解
文章目录 Broker详解 Broker概述 Broker启动和停止流程 Broker存储机制 为什么写文件这么快 Broker存储消息流程详解 (1)Broker接收客户端发送消息的请求并做预处理 ...
- Linux2.6 内核的 Initrd 机制解析
Linux2.6 内核的 Initrd 机制解析 级别: 初级 李 大治 (dazhi.li@gmail.com), 软件工程师 2006 年 5 月 23 日 Linux 的 initrd 技术是一 ...
- 关于javascript数据存储机制的一个案例。
之前在学习js的结合性的时候,我有点不太明白,在网上找到一个比较经典的C语言优先级结合性的案例,就是下边这一个.本想在js之中测试一番,结果竟然发现得出的结果和网上的不一样,这令我百思不得其解,后经高 ...
- 通过分析 JDK 源代码研究 Hash 存储机制
http://www.ibm.com/developerworks/cn/java/j-lo-hash/ 通过分析 JDK 源代码研究 Hash 存储机制 HashMap 和 HashSet 是 Ja ...
- 通过分析 JDK 源代码研究 Hash 存储机制--转载
通过 HashMap.HashSet 的源代码分析其 Hash 存储机制 集合和引用 就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并不是真正的把 Java 对象放入数组中,只是把对象 ...
- Linux内核的Softirq机制
前言 中断服务程序往往都是在CPU关中断的条件下执行的,以避免中断嵌套而使控制复杂化.但是CPU关中断的时间不能太长,否则容易丢失中断信号.为此,Linux将中断服务程序一分为二,各称作" ...
最新文章
- 2 数据源配置_论多数据源(读写分离)的实现方案
- 主板定制 如何定制主板这些流程要知道
- 报错:1130-host ... is not allowed to connect to this MySql server
- JavaWeb项目自动部署,持续集成
- A very hard mathematic problem(二分)
- 从君臣到战友:《谦逊领导力》就是敏捷领导力
- 第三方定量定性质谱检测技术实验
- [swift] UIImage NSImage PNG透明区域填充自定义颜色实现
- 云计算概念_云计算的概念
- oracle sequences优化_Oracle 优化和性能调整
- win10亮度无法调节问题
- FBTFT driver St7789v LCD 移植简介
- SEO搜索引擎方案制作与数字货币交易所SEO方案模版制作
- springboot+shiro自定义拦截器互踢问题
- 很好的英文资料书籍下载网站
- RTC时钟晶振为什么都是32.768K
- JavaScript输入圆的半径计算周长及面积
- Redis Cluster与Codis的选择
- 什么是激光雷达?及激光雷达主要应用在哪些领域?
- Sanic学习——初识Sanic