本文总结自:

  《PHP7 内核剖析 - 变量的内部实现》

一:变量的实现

  - 变量是一个语言实现的基础。

  - 在PHP中,变量的组成部分为 变量名(zval) 变量值(zend_value)

  - zval结构比较简单,内嵌一个union类型的zend_value保存具体变量类型的值或指针

  • //zend_types.h
    typedef struct _zval_struct zval; typedef union _zend_value {
    zend_long lval; //int整形
    double dval; //浮点型
    zend_refcounted *counted;
    zend_string *str; //string字符串
    zend_array *arr; //array数组
    zend_object *obj; //object对象
    zend_resource *res; //resource资源类型
    zend_reference *ref; //引用类型,通过&$var_name定义的
    zend_ast_ref *ast; //下面几个都是内核使用的value
    zval *zv;
    void *ptr;
    zend_class_entry *ce;
    zend_function *func;
    struct {
    uint32_t w1;
    uint32_t w2;
    } ww;
    } zend_value; struct _zval_struct {
    zend_value value; //变量实际的value
    union {
    struct {
    ZEND_ENDIAN_LOHI_4( //这个是为了兼容大小字节序,小字节序就是下面的顺序,大字节序则下面4个顺序翻转
    zend_uchar type, //变量类型
    zend_uchar type_flags, //类型掩码,不同的类型会有不同的几种属性,内存管理会用到
    zend_uchar const_flags,
    zend_uchar reserved) //call info,zend执行流程会用到
    } v;
    uint32_t type_info; //上面4个值的组合值,可以直接根据type_info取到4个对应位置的值
    } u1;
    union {
    uint32_t var_flags;
    uint32_t next; //哈希表中解决哈希冲突时用到
    uint32_t cache_slot; /* literal cache slot */
    uint32_t lineno; /* line number (for ast nodes) */
    uint32_t num_args; /* arguments number for EX(This) */
    uint32_t fe_pos; /* foreach position */
    uint32_t fe_iter_idx; /* foreach iterator index */
    } u2; //一些辅助值
    };

二:内存管理

  - 由于硬拷贝的效率太低

  - 所以PHP的变量管理是通过 引用计数,写时复制

三: 引用计数

  - 引用计数是指在value中增加一个字段refcount记录指向当前value的数量

  - 变量复制、函数传参时并不直接硬拷贝一份value数据,而是将refcount++,变量销毁时将refcount--,等到refcount减为0时表示已经没有变量引用这个value,将它销毁即可。

  - 从上面的zend_value结构可以看出并不是所有的数据类型都会用到引用计数,long、double直接都是硬拷贝,只有value是指针的那几种类型才__可能__会用到引用计数。

四:写时复制

  - 引用计数,多个变量可能指向同一个value,然后通过refcount统计引用数

  - 这时候如果其中一个变量试图更改value的内容则会重新拷贝一份value修改,同时断开旧的指向

  - 写时复制的机制在计算机系统中有非常广的应用,它只有在必要的时候(写)才会发生硬拷贝,可以很好的提高效率,

五: 变量回收

  - PHP变量的回收主要有两种:主动销毁、自动销毁。

  - 主动销毁指的就是 unset ,而自动销毁就是PHP的自动管理机制,在return时减掉局部变量的refcount,即使没有显式的return,PHP也会自动给加上这个操作

  - 另外一个就是写时复制时会断开原来value的指向,这时候也会检查断开后旧value的refcount。

六: 垃圾回收

  -  这是变量的简单gc过程,但是实际过程中出现gc无法回收导致内存泄漏的bug,先看下一个例子:

  - 

  -  可以看到,unset($a)之后由于数组中有子元素指向$a,所以refcount > 0,无法通过简单的gc机制回收

  - 这种变量就是垃圾,垃圾回收器要处理的就是这种情况。

  - 目前垃圾只会出现在array、object两种类型中。

  - 所以只会针对这两种情况作特殊处理:当销毁一个变量时,如果发现减掉refcount后仍然大于0,且类型是IS_ARRAY、IS_OBJECT则将此value放入gc可能垃圾双向链表中,等这个链表达到一定数量后启动检查程序将所有变量检查一遍,如果确定是垃圾则销毁释放。

《PHP内核剖析 - 变量/内存管理》的更多相关文章

  1. 简单物联网:外网访问内网路由器下树莓派Flask服务器

    最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...

  2. 利用ssh反向代理以及autossh实现从外网连接内网服务器

    前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...

  3. 外网访问内网Docker容器

    外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...

  4. 外网访问内网SpringBoot

    外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...

  5. 外网访问内网Elasticsearch WEB

    外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...

  6. 怎样从外网访问内网Rails

    外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...

  7. 怎样从外网访问内网Memcached数据库

    外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...

  8. 怎样从外网访问内网CouchDB数据库

    外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...

  9. 怎样从外网访问内网DB2数据库

    外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...

  10. 怎样从外网访问内网OpenLDAP数据库

    外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...

随机推荐

  1. RDay2-Problem 2 B

    题目描述 小明家有n个信箱,前前后后来送信和取信的总次数为q,称为q次访问,其中这q次访问分成三种类型. 1:邮递员送来了一封信,放在了x号信箱. 2:小明取走了x号信箱的所有信(x信箱可能已经没有信 ...

  2. PHP—-模型MODEL 一对多

    假如有两个表,菜品和菜品种类,菜品的关联外键是food_type_id, 所以在菜品的model中应该写 public function foodType(){ return $this->be ...

  3. Java框架中Struts和Struts2框架的区别

    struts1 与 struts2 的区别:1.都是 MVC 的 WEB 框架,2 struts1的老牌框架,应用很广泛,有很好的群众基础,使用它开发风险很小,成本更低!struts2虽然基于这个框架 ...

  4. ycmd for emacs 代码自动补全

    YCMD FOR EMACS Table of Contents 1. 安装 1.1. 下载 1.2. 安装相关依赖 1.3. 更新submodules 1.4. 安装 2. 配置 1 安装   1. ...

  5. Python学习(四十)—— Djago之认证系统

    一.COOKIE 与 SESSION 概念 cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要“保持状态”,因此cookie就是在这样一个场景下诞生. cook ...

  6. Python运算符——复合运算符

    就相当于算数运算符的后面加一个“=” 例:+= num = num+5   可以写成  num += 5 就是说,等式右边含有左边的变量名,就可以直接去掉,然后右边的符号移到左边去 同样的“-=  / ...

  7. [C#] 動的にアセンブリをロードする

    アプリケーション ドメインにおいて起動時に読み込まれない別のアセンブリ (.dll や .exe) を読み込む場合.System.Reflection.Assemby クラスの Load メソッドを使 ...

  8. H5实现全屏与F11全屏

    最近做项目用到全屏,现总结一下全屏: 1.局部全屏:H5全屏和F11有区别,在这种情况下判断全屏只需要通过H5全屏属性,无论全屏后有无滚动条都可判断. /** * [isFullscreen 判断浏览 ...

  9. Python matplotlib图片转化成矢量图并裁剪

    Python目前可以生成的图的格式是:eps, jpeg, jpg, pdf, pgf, png, ps, raw, rgba, svg, svgz, tif, tiff 在写论文时候我一般生成png ...

  10. URL 链接中 井号#、问号?、连接符& 分别有什么作用?

    在一个 URL 中可以包含很多的内容,其中不仅仅是包含 26 个英文字母,10 个罗马数字,中文汉字,还可以拥有井号“#”.问号“?”.连接符“&”等三种最常见的符号,那么这些符号在网站中都有 ...