本文总结自:

  《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. jquery前端倒计时

    function FreshTime(){ // var endtime=new Date("2019/04/15,12:20:12");//结束时间 var endtime = ...

  2. 检测浏览器是否支持ES6

    这是阮一峰老师写的一个工具 ES-Checker,用来检查各种运行环境对 ES6 的支持情况.访问ruanyf.github.io/es-checker,可以看到您的浏览器支持 ES6 的程度.运行下 ...

  3. eclipse格式化代码快捷键失效

    原因是与搜狗输入法的“简繁切换”快捷键冲突(取消搜狗输入法的简繁切换快捷键,即可解决)

  4. Intsall The Nessus in you kali linux

    1.first you shold download the nessus on the web station the nessus download site url:  https://www. ...

  5. Angular组件——投影

    运行时动态改变组件模版的内容.没路由那么复杂,只是一段html,没有业务逻辑. ngContent指令将父组件模版上的任意片段投影到子组件上. 一.简单例子 1.子组件中使用<ng-conten ...

  6. 【SQL】 MySql与SqlServer差异比较(MySql踩坑全集)

    本文主要记录将数据库从SqlServer移植到MySql的过程中,发现的各种坑爹问题.以SqlServer为主,记录MySql的差异性. 一.IF语句 首先MySql中的的IF语法不同. IF Con ...

  7. Base64 加解密

    import java.io.UnsupportedEncodingException; import org.apache.tomcat.util.codec.binary.Base64; /** ...

  8. Codeforces 1097E. Egor and an RPG game 构造

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF1097E.html 题解 首先我们求出 $k = f(n) = \max\{x|\frac{x(x+1)}2 ...

  9. 什么是vue

    1. 什么是vue.js? 1.1.vue.js是目前最火的一个前端框架,和Angular.js.React.js并称为前端三大主流框架. 1.2.Vue.js是有一套构建用户界面的框架,只关注视图层 ...

  10. HDFS及其各组件的机制

    一.HDFS运行机制 概述:用户的文件会被切块后存储在多台datanode节点中,并且每个文件在整个集群中存放多个副本,副本的数量可以通过修改配置自己设定. HDFS:Hadoop Distribut ...