0%

PHP7中zval的实现

“有点追求的PHPer都应该了解zval”

  1. ZVAL
  2. OPcache原理

本文完全是学习大牛博文的记录


前言

关于zval这块可以说涉及到非常多的知识,比如我们常见的引用计数、垃圾回收、写时复制。如果要深入的去理解这几个常见的问题,你需要学习了解PHP中变量是如何设计的。但我总喜欢问的干净点,为什么在PHP7中对int、double、bool、null不使用引用计数,string只是有些计数,有些不计数。如果不使用的话又如何进行垃圾回收?又或者说,对于单进程,生命周期结束就释放资源的PHP有必要进行垃圾回收吗?又是如何靠垃圾回收进行优化的?

本来一开始已经写好了有关PHP7中zval的结构,但是我们毕竟是站在巨人肩膀上的菜鸟,已经写不出比大佬更优秀的文章了。这里就记录一下阅读过程的总结和思考

第一篇·鸟哥

一个良好的设计, 一旦有了意外, 就会导致整个结构变得复杂, 维护性降低。——laruence

深入理解PHP7内核之zval——作者laruence

上半段

鸟哥的这篇文章分为两段,上半段主要介绍了六个方面的PHP5设计局限性:

  1. zval.value 在64bit机器下需要24个字节,而在32bit下需要16字节。zend_object_value最不常用,但却是最大的长板,因为它占到16字节

  2. 无预留字段,不方便实现复杂的功能

  3. 对象和资源(引用)传递时,需要一个全局引用计数才能保证它被回收。所以,在PHP5中它有两套引用计数

  4. 用到相同字符串时,只能复制整个zval

  5. 发生写时分离,导致数组复制,拖慢性能

  6. PHP5源码的tmp临时变量优化

下半段

文章的下半段详细介绍了PHP7zval的实现,以及解决的性能问题,我在这里顺序梳理一遍。

  • 现在PHP7的zval分为了三个部分,分别是负责存储类型信息(Type_Info)的u1,一些预留字段的u2,以及存储变量的值的zend_value。以上三个结构都是联合体,其中再有复杂的结构就用指针指向,所占字节不超过16字节。(zend_value 8字节,u1u2对齐占8字节,共16字节。ps:64bit机器C++ && 32bit同样)
  • zval的类型现在分为17种,值得注意的是TRUEFALSE是两种类型了
  • LONG,DOUBLE,NULL,FALSE,TRUE这些类型无需引用计数了
  • 对于复杂类型,仅需在zval中用一个指针指向该类型,引用计数也保存在该类型上。类似zend_string,zend_array,zend_refcounted这些结构体。
  • ZEND_ENDIAN_LOHI_4这个宏的作用是简化赋值(用来解决跨平台大小端问题的)
  • 关于常量类型和不可变类型等标志位的介绍
  • 最后一部分留了个坑,大概意思就是随着zval的内存分配方式改变,zval传递的方式也将改变。

第二篇·PHP开发组成员

[译]变量在 PHP7 内部的实现(一)

这篇文章翻译自国外PHP开发大佬的博客,内容上有重复,相对更加详细具体,挑着看就好了。

在浏览第二篇文章之前,可以试着先思考下面的这些问题,在文章中都可以找到答案。

  • C++中struct字节对齐是什么,union,struct所占字节大小计算方式,PHP7是如何利用字节对齐减少了zval所占大小?
  • 循环引用如何产生的,PHP5是如何解决的?
  • 什么是写时复制
  • 如果你了解PHP的引用计数,它是为了方便进行内存管理,实现垃圾回收。那么思考一下,既然Java有常量池的概念,并且PHP5.4后也引入了Interned String的概念,那么PHP有没有?它是如何实现的?

最后一个问题又引入了另一个概念:OPcache(越学不懂的越多,太真实了!)

第三篇·OPcache

OPcache工作原理

也是一篇好文章,浅显易懂。

  • OPcache 是Zend官方出品的缓存拓展,还具有代码优化功能,省去了每次加载和解析 PHP 脚本的开销。
  • 缓存两类内容:
    • OPCode(这里关于OPCode可以使用拓展VLM去了解)
    • Interned String,如注释、变量名等
  • OPCache缓存的机制主要是:将编译好的操作码放入共享内存,提供给其他进程访问
    • 文章还详细讲了共享内存,有兴趣可以深入了解
  • Interned String 缓存的内容包括: 变量名称、类名、方法名、字符串、注释等。
  • 文章的第5,6点接触不多,暂时感觉不到,先搁置。

多用GDB感受一下zval的结构!

好好学就是了!