我的编程空间,编程开发者的网络收藏夹
学习永远不晚

PHP7中zval结构和引用计数机制是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

PHP7中zval结构和引用计数机制是什么

这篇文章主要介绍PHP7中zval结构和引用计数机制是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

最近在查阅 PHP7 垃圾回收的资料的时候,网上的一些代码示例在本地环境下运行时出现了不同的结果,使我一度非常迷惑。 仔细一想不难发现问题所在:这些文章大多是 PHP5.x 时代的,而 PHP7 发布后,采用了新的 zval 结构,相关的资料也比较贫瘠,所以我结合一些资料做了一个总结,主要侧重于解释新 zval 容器中的引用计数机制,如有谬误,还望不吝指教。

PHP7 中新的 zval 结构

明人不说暗话,先看代码!

struct _zval_struct {    union {        zend_long         lval;                     double            dval;                     zend_refcounted  *counted;        zend_string      *str;        zend_array       *arr;        zend_object      *obj;        zend_resource    *res;        zend_reference   *ref;        zend_ast_ref     *ast;        zval             *zv;        void             *ptr;        zend_class_entry *ce;        zend_function    *func;        struct {            uint32_t w1;            uint32_t w2;        } ww;    } value;    union {        struct {            ZEND_ENDIAN_LOHI_4(                zend_uchar    type,                         zend_uchar    type_flags,                zend_uchar    const_flags,                zend_uchar    reserved)             } v;        uint32_t type_info;    } u1;    union {        uint32_t     var_flags;        uint32_t     next;                         uint32_t     cache_slot;                   uint32_t     lineno;                       uint32_t     num_args;                     uint32_t     fe_pos;                       uint32_t     fe_iter_idx;              } u2;};复制代码

对于该结构的详细描述可以参考文末鸟哥的文章,写的非常详细,我就不关公面前耍大刀了,这里我只提出几个比较关键的点:

  • PHP7 中的变量分为变量名变量值两部分,分别对应 zval_struct 和在其中声明的 value

  • zval_struct.value 中的 zend_longdouble 都是简单数据类型,能够直接储存具体的值,而其他复杂数据类型储存一个指向其他数据结构的指针

  • PHP7 中,引用计数器储存在 value 中而不是 zval_struct

  • NULL布尔型都属于没有值的数据类型(其中布尔型通过 IS_FALSEIS_TRUE 两个常量来标记),自然也就没有引用计数

  • 引用(REFERENCE)变为了一种数据结构而不再只是一个标记位了,它的结构如下:

struct _zend_reference {    zend_refcounted_h gc;    zval              val;}
  • zend_reference 作为 zval_struct 中包含的一种 value 类型,也拥有自己的 val 值,这个值是指向一个 zval_struct.value 的。他们都拥有自己的引用计数器

引用计数器用来记录当前有多少 zval 指向同一个 zend_value

针对第六点,请看如下代码:

$a = 'foo';$b = &$a;$c = $a;

此时的数据结构是这样的:

$a 与 $b 各拥有一个 zval_struct 容器,并且其中的 value 都指向同一个 zend_reference 结构,zend_reference 内嵌一个 val 结构, 指向同一个 zend_string字符串的内容就储存在其中。

而 $c 也拥有一个 zval_struct,而它的 value 在初始化的时候可以直接指向上面提到的 zend_string ,这样在拷贝时就不会产生复制。

下面我们就聊一聊在这种全新的 zval 结构中,会出现的种种现象,和这些现象背后的原因。

问题

一. 为什么某些变量的引用计数器的初始值为 0

现象
$var_int = 233;$var_float = 233.3;$var_str = '233';xdebug_debug_zval('var_int');xdebug_debug_zval('var_float');xdebug_debug_zval('var_str');
原因

在 PHP7 中,为一个变量赋值的时候,包含了两部分操作:

为符号量(即变量名)申请一个 zval_struct 结构

将变量的值储存到 zval_struct.value 中 对于 zvalvalue 字段中能保存下的值,就不会在对他们进行引用计数,而是在拷贝的时候直接赋值,这部分类型有:

  • IS_LONG

  • IS_DOUBLE

即我们在 PHP 中的整形浮点型

那么 var_str 的 refcount 为什么也是 0 呢?  
这就牵扯到 PHP 中字符串的两种类型:

interned string 内部字符串(函数名、类名、变量名、静态字符串):

 $str = '233';    // 静态字符串

普通字符串:

 $str = '233' . time();

对于内部字符串而言,字符串的内容是唯一不变的,相当于 C 语言中定义在静态变量区的字符串,他们的生存周期存在于整个请求期间,request 完成后会统一销毁释放,自然也就无需通过引用计数进行内存管理。

二. 为什么在对整形、浮点型和静态字符串型变量进行引用赋值时,计数器的值会直接变为2

现象
$var_int_1 = 233;$var_int_2 = &var_int;xdebug_debug_zval('var_int_1');
原因

回忆一下我们开头讲的 zval_structvalue 的数据结构,当为一个变量赋整形浮点型静态字符串类型的值时,value 的数据类型为 zend_longdoublezend_string,这时值是可以直接储存在 value 中的。而按值拷贝时,会开辟一个新的 zval_struct 以同样的方式将值储存到相同数据类型的 value 中,所以 refcount 的值一直都会为 0。

但是当使用 & 操作符进行引用拷贝时,情况就不一样了:

  • PHP 为 & 操作符操作的变量申请一个 zend_reference 结构

  • zend_reference.value 指向原来的 zval_struct.value

  • zval_struct.value 的数据类型会被修改为 zend_refrence

  • zval_struct.value 指向刚刚申请并初始化后的 zend_reference

  • 为新变量申请 zval_struct 结构,将他的 value 指向刚刚创建的 zend_reference

此时:$var\_int\_1 和 $var_int_2 都拥有一个 zval_struct 结构体,并且他们的 zval_struct.value 都指向了同一个 zend_reference 结构,所以该结构的引用计数器的值为 2。

题外话:zend_reference 又指向了一个整形或浮点型的 value,如果指向的 value 类型是 zend_string,那么该 value 引用计数器的值为 1。而 xdebug 出来的 refcount 显示的是 zend_reference 的计数器值(即 2)

三. 为什么初始数组的引用计数器的值为 2

现象
$var_empty_arr = [1, 2, '3'];xdebug_debug_zval('var_empty_arr');
原因

这牵扯到 PHP7 中的另一个概念,叫做 immutable array(不可变数组)。

For arrays the not-refcounted variant is called an "immutable array". If you use opcache, then constant array literals in your code will be converted into immutable arrays. Once again, these live in shared memory and as such must not use refcounting. Immutable arrays have a dummy refcount of 2, as it allows us to optimize certain separation paths.

不可变数组opcache 扩展优化出的一种数组类型,简单的说,所有多次编译结果恒定不变的数组,都会被优化为不可变数组,下面是一个反例:

$array = [1, 2, time()];

PHP 在编译阶段无法得知 time() 函数的返回值,所以此处的 $array 是可变数组

不可变数组和我们上面讲到的内部字符串一样,都是不使用引用计数的,但是不同点是,内部字符串的计数值恒为 0,而不可变数组会使用一个伪计数值 2。

总结

  • 简单数据类型

    • 整形(不使用引用计数)

    • 浮点型(不使用引用计数)

    • 布尔型(不使用引用计数)

    • NULL(不使用引用计数)

  • 复杂数据类型

    • 普通数组(使用引用计数,初始值为 1)

    • 不可变数组(不使用引用计数,使用伪计数值 2)

    • 普通字符串(使用引用计数,初始值为 1)

    • 内部字符串(不使用引用计数,引用计数值恒为 0)

    • 字符串

    • 数组

    • 对象(使用引用计数,初始值为 1)

以上是“PHP7中zval结构和引用计数机制是什么”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注编程网行业资讯频道!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

PHP7中zval结构和引用计数机制是什么

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

PHP7中zval结构和引用计数机制是什么

这篇文章主要介绍PHP7中zval结构和引用计数机制是什么,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!最近在查阅 PHP7 垃圾回收的资料的时候,网上的一些代码示例在本地环境下运行时出现了不同的结果,使我一度非常迷
2023-06-14

python中什么是引用计数器机制

本篇文章为大家展示了python中什么是引用计数器机制,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。python可以做什么Python是一种编程语言,内置了许多有效的工具,Python几乎无所不能,
2023-06-14

Python引用计数机制是什么

Python的引用计数机制是一种自动内存管理机制,用于跟踪对象的引用数量。每个对象都有一个引用计数,当一个对象被创建时,引用计数为1;每当一个新的引用指向该对象时,引用计数就会增加1;当一个引用被删除时,引用计数就会减少1。当一个对象的引用
2023-10-25

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录