“有点追求的PHPer都应该了解zval”
- ZVAL
- OPcache原理
本文完全是学习大牛博文的记录
前言
关于zval这块可以说涉及到非常多的知识,比如我们常见的引用计数、垃圾回收、写时复制。如果要深入的去理解这几个常见的问题,你需要学习了解PHP中变量是如何设计的。但我总喜欢问的干净点,为什么在PHP7中对int、double、bool、null不使用引用计数,string只是有些计数,有些不计数。如果不使用的话又如何进行垃圾回收?又或者说,对于单进程,生命周期结束就释放资源的PHP有必要进行垃圾回收吗?又是如何靠垃圾回收进行优化的?
本来一开始已经写好了有关PHP7中zval的结构,但是我们毕竟是站在巨人肩膀上的菜鸟,已经写不出比大佬更优秀的文章了。这里就记录一下阅读过程的总结和思考
第一篇·鸟哥
一个良好的设计, 一旦有了意外, 就会导致整个结构变得复杂, 维护性降低。——laruence
上半段
鸟哥的这篇文章分为两段,上半段主要介绍了六个方面的PHP5设计局限性:
zval.value 在64bit机器下需要24个字节,而在32bit下需要16字节。
zend_object_value
最不常用,但却是最大的长板,因为它占到16字节无预留字段,不方便实现复杂的功能
对象和资源(引用)传递时,需要一个全局引用计数才能保证它被回收。所以,在PHP5中它有两套引用计数
用到相同字符串时,只能复制整个zval
发生写时分离,导致数组复制,拖慢性能
PHP5源码的tmp临时变量优化
下半段
文章的下半段详细介绍了PHP7zval的实现,以及解决的性能问题,我在这里顺序梳理一遍。
- 现在PHP7的
zval
分为了三个部分,分别是负责存储类型信息(Type_Info
)的u1
,一些预留字段的u2
,以及存储变量的值的zend_value
。以上三个结构都是联合体,其中再有复杂的结构就用指针指向,所占字节不超过16字节。(zend_value
8字节,u1
,u2
对齐占8字节,共16字节。ps:64bit机器C++ && 32bit同样) - zval的类型现在分为17种,值得注意的是
TRUE
和FALSE
是两种类型了 LONG,DOUBLE,NULL,FALSE,TRUE
这些类型无需引用计数了- 对于复杂类型,仅需在zval中用一个指针指向该类型,引用计数也保存在该类型上。类似
zend_string,zend_array,zend_refcounted
这些结构体。 ZEND_ENDIAN_LOHI_4
这个宏的作用是简化赋值(用来解决跨平台大小端问题的)- 关于常量类型和不可变类型等标志位的介绍
- 最后一部分留了个坑,大概意思就是随着zval的内存分配方式改变,zval传递的方式也将改变。
第二篇·PHP开发组成员
这篇文章翻译自国外PHP开发大佬的博客,内容上有重复,相对更加详细具体,挑着看就好了。
在浏览第二篇文章之前,可以试着先思考下面的这些问题,在文章中都可以找到答案。
- C++中
struct
字节对齐是什么,union,struct
所占字节大小计算方式,PHP7是如何利用字节对齐减少了zval
所占大小? - 循环引用如何产生的,PHP5是如何解决的?
- 什么是写时复制
- 如果你了解PHP的引用计数,它是为了方便进行内存管理,实现垃圾回收。那么思考一下,既然Java有常量池的概念,并且PHP5.4后也引入了Interned String的概念,那么PHP有没有?它是如何实现的?
最后一个问题又引入了另一个概念:OPcache(越学不懂的越多,太真实了!)
第三篇·OPcache
也是一篇好文章,浅显易懂。
- OPcache 是Zend官方出品的缓存拓展,还具有代码优化功能,省去了每次加载和解析 PHP 脚本的开销。
- 缓存两类内容:
- OPCode(这里关于OPCode可以使用拓展
VLM
去了解) - Interned String,如注释、变量名等
- OPCode(这里关于OPCode可以使用拓展
- OPCache缓存的机制主要是:将编译好的操作码放入共享内存,提供给其他进程访问。
- 文章还详细讲了共享内存,有兴趣可以深入了解
- Interned String 缓存的内容包括: 变量名称、类名、方法名、字符串、注释等。
- 文章的第5,6点接触不多,暂时感觉不到,先搁置。
多用GDB感受一下zval的结构!
好好学就是了!