1.变量结构

typedef struct _zval_struct     zval;typedef union _zend_value {zend_long         lval;    //int整形double            dval;    //浮点型zend_string      *str;     //string字符串zend_array       *arr;     //array数组zend_object      *obj;     //object对象zend_resource    *res;     //resource资源类型zend_reference   *ref;     //引用类型,通过&$var_name定义的
} zend_value;struct _zval_struct {zend_value        value; //变量实际的valueunion {struct {ZEND_ENDIAN_LOHI_4( zend_uchar    type,         //变量类型zend_uchar    type_flags,  //类型掩码,不同的类型会有不同的几种属性,内存管理会用到zend_uchar    const_flags,zend_uchar    reserved)} v;uint32_t type_info; //上面4个值的组合值,可以直接根据type_info取到4个对应位置的值} 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;
};

2.变量类型

#define IS_UNDEF                    0
#define IS_NULL                     1
#define IS_FALSE                    2
#define IS_TRUE                     3
#define IS_LONG                     4
#define IS_DOUBLE                   5
#define IS_STRING                   6
#define IS_ARRAY                    7
#define IS_OBJECT                   8
#define IS_RESOURCE                 9
#define IS_REFERENCE                10

其中undef、true、false、null没有value,直接根据type区分,而long、double的值则直接存在value中,其他类型为指针

3.字符串

typedef struct _zend_string   zend_string;struct _zend_string {zend_refcounted_h gc;  //变量引用信息,比如当前value的引用数size_t            len;  //字符串长度,通过这个值保证二进制安全char              val[1]; //字符串内容,变长struct,分配时按len长度申请内存
};

4.数组

typedef struct _zend_array HashTable;
typedef struct _zend_array zend_array;typedef struct _Bucket {zval              val; //存储的具体value,这里嵌入了一个zval,而不是一个指针zend_ulong        h;   //哈希值zend_string      *key; //key值
} Bucket;struct _zend_array {zend_refcounted_h gc; //引用计数信息uint32_t          nTableMask;  //计算bucket索引时的掩码,用于散列表的计算nIndexBucket           *arData;     //bucket数组uint32_t          nNumUsed;   //已用bucket数uint32_t          nNumOfElements; //已有元素数,nNumOfElements <= nNumUsed,因为删除的并不是直接从arData中移除uint32_t          nTableSize; //数组的大小,为2^n,默认为8uint32_t          nInternalPointer; //数值索引,用于HashTable遍历zend_long         nNextFreeElement;//下一个空闲可用位置的数字索引dtor_func_t       pDestructor;//析构函数,销毁时调用的函数指针
};

HashTable主要依赖arData实现元素的存储、索引。插入一个元素时先将元素按先后顺序插入Bucket数组,位置是idx,再根据key的哈希值映射到散列表中的某个位置nIndex,将idx存入这个位置;查找时先在散列表中映射到nIndex,得到value在Bucket数组的位置idx,再从Bucket数组中取出元素。

$arr["a"] = 1;
$arr["b"] = 2;
$arr["c"] = 3;
$arr["d"] = 4;unset($arr["c"]);

哈希碰撞:当出现冲突时将原value的位置保存到新value的zval.u2.next中,然后将新value代替原value位置

扩容:PHP散列表的大小为2^n,插入时如果容量不够则首先检查已删除元素所占比例,如果达到阈值,则将已删除元素移除,重建索引,如果未到阈值则进行扩容操作,扩大为当前大小的2倍,将当前Bucket数组复制到新的空间,然后重建索引。

重建散列表:当删除元素达到一定数量或扩容后都需要重建散列表,因为value在Bucket位置移动了或哈希数组nTableSize变化了导致key与value的映射关系改变,重建过程实际就是遍历Bucket数组中的value,然后重新计算映射值更新到散列表,移除已删除的value,将后面未删除的value依次前移

5.引用

引用是PHP中比较特殊的一种类型,它实际是指向另外一个PHP变量,对它的修改会直接改动实际指向的zval,可以简单的理解为C中的指针,在PHP中通过&操作符产生一个引用变量,也就是说不管以前的类型是什么,&首先会创建一个zend_reference结构,其内嵌了一个zval,这个zval的value指向原来zval的value(如果是布尔、整形、浮点则直接复制原来的值),然后将原zval的类型修改为IS_REFERENCE,原zval的value指向新创建的zend_reference结构。

typedef struct _zend_reference  zend_reference;struct _zend_reference {zend_refcounted_h gc;zval              val;
};

6.引用计数

typedef struct _zend_refcounted_h {uint32_t         refcount;         union {struct {ZEND_ENDIAN_LOHI_3(zend_uchar    type,zend_uchar    flags,   uint16_t      gc_info)  } v;uint32_t type_info;} u;
} zend_refcounted_h;
$a = "time:" . time();   //$a       ->  zend_string_1(refcount=1)
$b = $a;                 //$a,$b    ->  zend_string_1(refcount=2)
$c = $b;                 //$a,$b,$c ->  zend_string_1(refcount=3)unset($b);               //$b = IS_UNDEF  $a,$c ->  zend_string_1(refcount=2)

并不是所有的数据类型都会用到引用计数,long、double直接都是硬拷贝,只有value是指针的那几种类型(除interned string,immutable array)才能用到引用计数。可由zval.u1.type_flag判断

7.写时复制

$a = array(1,2);
$b = &$a;
$c = $a;//发生分离
$b[] = 3;

事实上只有string、array两种支持,

8.垃圾回收

PHP变量的回收主要有两种:主动销毁、自动销毁。主动销毁指的就是 unset ,而自动销毁就是PHP的自动管理机制,在return时减掉局部变量的refcount,即使没有显式的return,PHP也会自动给加上这个操作,另外一个就是写时复制时会断开原来value的指向,这时候也会检查断开后旧value的refcount。

$a = [1];
$a[] = &$a;unset($a);

unset($a)之前引用关系:

unset($a)之后:

可以看到,unset($a)之后由于数组中有子元素指向$a,所以refcount > 0,无法通过简单的gc机制回收,这种变量就是垃圾,垃圾回收器要处理的就是这种情况,目前垃圾只会出现在array、object两种类型中,所以只会针对这两种情况作特殊处理:当销毁一个变量时,如果发现减掉refcount后仍然大于0,且类型是IS_ARRAY、IS_OBJECT则将此value放入gc可能垃圾双向链表中,等这个链表达到一定数量后启动检查程序将所有变量检查一遍,如果确定是垃圾则销毁释放。

目前只有object、array两种类型会使用这种机制。

(PHP7内核剖析-3) 变量相关推荐

  1. PHP7内核基础知识之变量类型

    前言 下面我们大概了解下PHP7的变量类型都有哪些,是如何存储变量的. zval结构定义 PHP7中是使用zval结构存储变量信息的.zval结构的定义在./Zend/zend_types.h文件中定 ...

  2. 《Linux内核剖析》(Yanlz+VR云游戏+Unity+SteamVR+云技术+5G+AI+Makefile+块设备驱动+字符设备驱动+数学协处理器+文件系统+内存管理+GDB+立钻哥哥+==)

    <Linux内核剖析> <Linux内核剖析> 版本 作者 参与者 完成日期 备注 YanlzLinux_Kernel0.12_V01_1.0 严立钻 2020.02.06 # ...

  3. 索骥馆-编程语言之《Android内核剖析》扫描版[PDF]

    内容介绍: <android内核剖析>详细分析了android内核的内部机制,包括窗口管理系统.activity管理系统.输入法框架.编译系统等,为android内核定制及高级应用程序开发 ...

  4. PHP内核探索之变量(1)Zval

    原文:PHP内核探索之变量(1)Zval 作为数据的容器,我们常常需要跟变量打交道,不管这个变量是数字.数组.字符串.对象还是其他,因而可以说变量是构成语言的不可或缺的基础.本文是PHP内核探索之变量 ...

  5. PHP内核探索之变量(2)-理解引用

    PHP内核探索之变量(2)-理解引用 原文:PHP内核探索之变量(2)-理解引用 本文主要内容: 引论 符号表与zval 引用原理 回到最初的问题 一.引论 很久之前写了一篇关于引用的文章,当时写的寥 ...

  6. PHP内核探索之变量(4)- 数组操作

    原文:PHP内核探索之变量(4)- 数组操作 上一节(PHP内核探索之变量(3)- hash table),我们已经知道,数组在PHP的底层实际上是HashTable(链接法解决冲突),本文将对最常用 ...

  7. PHP内核探索之变量(1)Zval(自己看过不错儿)

    作为数据的容器,我们常常需要跟变量打交道,不管这个变量是数字.数组.字符串.对象还是其他,因而可以说变量是构成语言的不可或缺的基础.本文是PHP内核探索之变量的第一篇,主要介绍zval的基本知识,包括 ...

  8. 查询优化器内核剖析第一篇

    查询优化器内核剖析第一篇 查询优化器内核剖析第一篇 查询优化器内核剖析第二篇:产生候选执行计划&执行计划成本估算 查询优化器内核剖析第三篇:查询的执行与计划的缓存 & Hint提示 查 ...

  9. Android内核剖析

    -- Android内核剖析  柯元旦 编著 ISBN 978-7-121-14398-4   2011年9月出版 定价:79.90元 16开 616页 内容简介: 本书内容分别从基础.内核.系统.编 ...

最新文章

  1. docker 集群中文件挂载的问题
  2. vue里写三元判断绑定class和style
  3. linux文件基础知识,linux文件系统基础知识
  4. 11 PopupMenu菜单和代码例子
  5. CentOS配置snmp
  6. 卸载python27_27. 移除元素(Python)
  7. 最新eclipse国内镜像站,比ustc等站点资源新。
  8. SpringBoot 的错误处理机制
  9. MLOps- 吴恩达Andrew Ng Overview of the ML Lifecycle and Deployment Week1 部署深度学习模型model 实现作业
  10. 软件管理员密码忘记怎么办?软件密码如何找回?
  11. Cheat Engine CE官方教程 [汉化]
  12. 微信企业消息推送方案
  13. 图片格式如何批量转换成jpg呢?
  14. php 将中文字符转英文字母_php中怎么将中文转换拼音
  15. android ppt 转图片显示不全,ppt转pdf图片显示不全怎么办
  16. 苏州大学 计算机网络,苏州大学计算机网络与通信期末考试卷-20210517192500.docx-原创力文档...
  17. 我们如何造红色敞篷跑车
  18. 秒针计时器 html,JS实现一个秒表计时器
  19. 七年级 电子计算机 教材分析,七年级信息技术教学计划表
  20. 传统分割方法汇总(包括SLIC、Ncut、watershed、graph-based segmentation、Meanshit、最大熵分割)

热门文章

  1. UICollectionView入门--使用系统UICollectionViewFlowLayout布局类
  2. (三十一)java多线程二
  3. php math函数
  4. 当代大学生的变态生活
  5. 11、集合--Set接口
  6. 从前端程序员的视角看小程序的稳定性保障
  7. 从 Java 档案(JAR) 中读取文件
  8. 在Mybatis3开发中与配置相关的7点体会
  9. 移动APP的自动化测试
  10. 安装yarn 心得分享