《PHP内核剖析 - 变量/内存管理》
本文总结自:
一:变量的实现
- 变量是一个语言实现的基础。
- 在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内核剖析 - 变量/内存管理》的更多相关文章
- 简单物联网:外网访问内网路由器下树莓派Flask服务器
最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...
- 利用ssh反向代理以及autossh实现从外网连接内网服务器
前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...
- 外网访问内网Docker容器
外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...
- 外网访问内网SpringBoot
外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...
- 外网访问内网Elasticsearch WEB
外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...
- 怎样从外网访问内网Rails
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- 怎样从外网访问内网CouchDB数据库
外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...
- 怎样从外网访问内网DB2数据库
外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...
- 怎样从外网访问内网OpenLDAP数据库
外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...
随机推荐
- android SDK模拟器环境搭建
一.下载安装android SDK 两种方式: (1)官网下载(需FQ):https://developer.android.com/studio/index.html (2)无需FQ下载:http: ...
- Windows平台下结合 tortoiseSVN 和 VisualSVN Server 搭建SVN服务器并实现 web 站点同步
1. tortoiseSVN 关于 tortoiseSVN 的安装使用详见博文 TortoiseSVN的安装及其简单使用. 2. VisualSVN Server 关于 VisualSVN Serve ...
- Java_Runtime&Process&ProcessBuilder
目录 一.Runtime类 二.Process类 三.ProcessBuilder类 在Java中想调用外部程序,或者执行命令和可运行文件时,网上的典型实例一般都是通过Runtime.getTime( ...
- python实现简单的购物车
import json,timeuserinfo={"lanfei": { "passwd":"lanfei", "salary& ...
- 整理一下python中with的用法
ith替代了之前在python里使用try...finally来做清理工作的方法.基本形式如下: with expression [as variable]: with-block 当expressi ...
- C++ vector的用法(整理)
vector 是向量类型,它可以容纳许多类型的数据,如若干个整数,所以称其为容器.vector 是C++ STL的一个重要成员,使用它时需要包含头文件: #include<vector>; ...
- JDK配置环境变量不成功的原因
根据自己配置环境变量遇到的问题进行总结: 1.二次安装需要注意的问题 由于已经安装了一次的原因,此时的注册表已经有了安装记录. 建议删除jdk的记录 首先打开注册表 开始菜单→运行.或者直接键盘按下W ...
- Phabricator服务的搭建
目录 概述 遇到的坑-Phabricator连接不上MySQL 准备环境 安装软件 安装Nginx 安装MySQL 安装PHP 下载Phabricator 配置 配置nginx和php 配置phabr ...
- [转] Java程序员学C#基本语法两个小时搞定(对比学习)
Java程序员学C#基本语法两个小时搞定(对比学习) 对于学习一门新的语言,关键是学习新语言和以前掌握的语言的区别,但是也不要让以前语言的东西,固定了自己的思维模式,多看一下新的语言的编程思想. ...
- Python 小知识 杂七杂八 随手记
1.assert 断言语句 例1: print ‘11111111111’ assert 1==2 print ‘22222222’ 如果没有 assert 程序会输出 ‘1111111111 ...