php内存变量的值,对PHP变量的实现方式以及内存管理的梳理

原标题:对PHP变量的实现方式以及内存管理的梳理

变量

局部变量 PHP中局部变量分配在zend_execute_data结构上,每次执行zend_op_array都会生成一个新的zend_execute_data 局部变量通过编译时确定的编号进行读写操作

静态变量 静态变量只会在编译时初始化,保存在zend_op_array->static_variables 这个哈希表中 静态变量通过哈希表保存,这就使得能像普通变量那样有一个固定的编号 编译时先判断zend_op_array->static_variables 是否已创建,然后将静态变量插入哈希表

//zend_compile_static_var_common():

if (!CG(active_op_array)->static_variables) {

ALLOC_HASHTABLE(CG(active_op_array)->static_variables);

zend_hash_init(CG(active_op_array)->static_variables, 8, NULL, ZVAL_P

TR_DTOR, 0);

}

//插入静态变量

zend_hash_update(CG(active_op_array)->static_variables, Z_STR(var_node.u.

constant), value);

例如$count与 static_variables["count"]间的关系如图所示

5676db31d300c5003b97590f25e0478d.png

垃圾回收 一个是引用计数这个早期就有的基本机制,refcount减到0时,释放变量 这里同时也介绍下一个比较通用的写时复制机制,$a = 1;

$b = $a; // 这里变量$a 与变量$b 持有的是同一个zend_val

$a = 2; // 这个时候变量$a的值发生了改变,而显然,让$b的值也发生同样的改变是不符合预期的 所以这个时候就会发生zend_val的复制

//另外一种情况

$a = 1;

$b = &$a; //当$b只有的是对$a的引用时,这两个变量始终共用同一个zend_val

$a = 2; //这时$b的值也为2

循环引用 引用计数机制有一个缺陷,就是碰到循环引用时,refcount无法减到0,导致变量无法释放,具体来说就是变量内部的成员引用了变量本身,比如数组中的某个元素指向了数组$a = [1];

$a[] = &$a;

unset($a);

针对这种情况,php引入了垃圾回收器来处理 变量是否加入垃圾检查buffer并不是根据zval的类型判断的,而是与前面介绍的是否用到引用计数一样通过 zval.u1.type_flag 记录的,只有包含 IS_TYPE_COLLECTABLE 的变量才会被GC收集

目前垃圾只会出现在array、object两种类型中,只有这两种类型的变量会出现成员引用自身的情况

如果当变量的refcount减少后大于0,PHP并不会立即进行对这个变量进行垃圾鉴定,而是放入一个缓冲buffer中,等这个buffer满了以后(10000个值)再统一进行处理,加入buffer的是 变量zend_value的 zend_refcounted_h

zend_refcounted_h.gc_info 置为 GC_PURPLE ,即标为紫色,下次refcount减少时

如果发现已经加入过了则不再重复插入。

垃圾缓存区是一个双向链表,等到缓存区满了以后则启动垃圾检查过程:遍历缓存区,再对当前变量的所有成员进行遍历,然后把成员的refcount减1(如果成员还包含子成员则也进行递归遍历,其实就是深度优先的遍历)

最后再检查当前变量的引用,如果减为了0则为垃圾

这个算法的原理很简单,垃圾是由于成员引用自身导致的,那么就对所有的成员减一遍引用,结果如果发现变量本身refcount变为了0则就表明其引用全部来自自身成员。

PHP对象在内存堆栈中的分配

对象在PHP里面和整型、浮点型一样,也是一种数据类,都是存储不同类型数据用的, 在运行的时候都要加载到内存中去用,那么对象在内存里面是怎么体现的呢?内存从逻辑上说大体上是分为4段,栈空间段、堆空间段、代码段、初始化静态段,程序里面不同的声明放在不同的内存段里面。

数据段(data segment)通常是指用来存放程序中已初始化且不为0的全局变量如:静态变量和常量

代码段(code segment / text segment)通常是指用来存放程序执行代码的一块内存区域,比如函数和方法

栈空间段是存储占用相同空间长度并且占用空间小的数据类型的地方,比如说整型1,10,100,1000,10000,100000 等等,在内存里面占用空间是等长的,都是64 位4 个字节。

(heap)数据长度不定长,而且占有空间很大的数据类型的数据放在堆内存里面的。

栈内存是可以直接存取的,而堆内存是 不可以直接存取的内存。对于我们的对象来数就是一种大的数据类型而且是占用空间不定长的类型,所以说对象是放在堆里面的,但对象名称是放在栈里面的,这样通过对象名称就可 以使用对象了。

PHP脚本运行的时候,那些变量被放到了栈内存,那些被保存到了堆内存?

在PHP5的Zend Engine的实现中,所有的值都是在堆上分配空间,并且通过引用计数和垃圾收集来管理. PHP5的Zend Engine主要使用指向zval结构的指针来操作值,在很多地方甚至通过zval的二级指针来操作.

而在PHP7的Zend Engine实现中,值是通过zval结构本身来操作(非指针). 新的zval结构直接被存放在VM[虚拟机?]的栈上,HashTable的桶里,以及属性槽里. 这样大大减少了在堆上分配和释放内存的操作,还避免了对简单值的引用计数和垃圾收集.

引用:

PHP对象在内存堆栈中的分配 - web21 - 博客园

《PHP7内核剖析》返回搜狐,查看更多

责任编辑:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值