变量的内部存储:引用和计数

垃圾标记阶段--引用计数算法、可达性分析算法 前言: 对象存活判断 在垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象。只有对死亡对象的标记才会被GC释放空间,因此这个过程可以称为垃圾标记阶段。 简单来说,当一个对象已经不再被任何的存活对象继续引用时,就可以宣判为已经死亡。 判断对象的存活一般有两种方式:引用计数算法 和 可达性分析算法。 一、引用计数算法(Java未使用) 1、原理: 引用计数算法,对每个对象保存一个整型的引用计数器属性。用于记录对象被引用的情况。 对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就 阅读详情

变量的内部引用和计数   

    在引擎内部,一个PHP的变量是保存在“zval”结构中,此结构包含了变量的类型和值信息,这个在之前的文章  变量的内部存储:值和类型 中已经介绍了,此结构还有另外两个字段信息,一个是"is_ref"(此字段在5.3.2版本中是is_ref__gc),此字段是一个布尔值,用来标识变量是否是一个引用,通过这个字段,PHP引擎能够区分一般的变量和引用变量。PHP代码中可以通过 & 操作符号来建立一个引用变量,建立的引用变量内部的zval的is_ref字段就为1。zval中还有另外一个字段refcount(此字段在5.3.2版本中是refcount__gc),这个字段是一个计数器,表示有多少个变量名指向这个zval容器,当此字段为0时,表示没有任何变量指向这个zval,那么zval就可以被释放,这是引擎内部对内存的一种优化。考虑如下代码:

 

代码中有两个变变量$a和$b,通过普通赋值方式将$a赋给$b,这样$b的值和$a相等,对$b的修改不会对$a造成任何影响,那么在这段代码中,如果$a和$b对应两个不同的zval,那么显然是对内存的一种浪费,PHP的开发者也不会让这样的事情发生。所以实际上$a和$b是指向同一个zval。这个zval的类型是STRING,值是"Hello world",有$a和$b两个变量指向它,所以它的refcount=2, 由于是一个普通赋值,所以is_ref字段为0。 这样就节省了内存开销。

当执行$a = "Hello world"之后,$a对应的zval的信息为:

a: (refcount=1, is_ref=0)="Hello world"

但执行$b=$a之后,$a对应的zval的信息为:

a: (refcount=2, is_ref=0)="Hello world"

 

下面将之前的代码修改一下:

 

这样就通过引用赋值方式将$a赋给$b

当执行$a = "Hello world"之后,$a对应的zval的信息为:

a: (refcount=1, is_ref=0)="Hello world"

但执行$b=&$a之后,$a对应的zval的信息为:

a: (refcount=2, is_ref=1)="Hello world"

可以发现is_ref字段被设置成1了,这样$a和$b对应的zval就是一个引用。

 

这样我们基本对引擎中变量的引用和计数有了一个基本的了解,下面将介绍变量的分离。

 

变量的分离:copy on write 

考虑前面第一段代码,用普通方式将$a赋给$b,在内部两个变量还是指向同一个zval的,这个时候如果我们将$b的值修改为"new string",$a变量的值依然是"Hello world":

 

$a和$b明明是指向同一个zval,为什么修改了$b,$a还能保持不变呢,这就是copy on write(写时复制)技术,简单的说,当重新给$b赋值的时候,会将$b从之前的zval中分离出来。分离之后,$a和$b分别是指向不同的zval了。

(写时复制技术的一个比较有名的应用是在unix类操作系统内核中,当一个进程调用fork函数生成一个子进程的时候,父子进程拥有相同的地址空间内容,在老版本的系统中,子进程是在fork的时候就将父进程的地址空间中的内容都拷贝一份,对于规模较大的程序这个过程可能会有着很大的开销,更崩溃的是,很多进程在fork之后,直接在子进程中调用exec执行另外一个程序,这样原来花了大量时间从父进程复制的地址空间都还没来得及碰一下就被新的进程地址空间代替,这显然是对资源的极大浪费,所以在后来的系统中,就使用了写时复制技术,fork之后,子进程的地址空间还是简单的指向父进程的地址空间,只有当子进程需要写地址空间中的内容的时候,才会单独分离一份(一般以内存页为单位)给子进程,这样就算子进程马上调用exec函数也没关系,因为根本就不需要从父进程的地址空间中拷贝内容,这样节约了内存同时又提高了速度)

 

当$b从$a指向的zval分离出来之后,zval的refcount就要减1,这样由之前的2变成了1,表示这个zval还有一个变量指向它,就是$a。$b变量指向了一个新的zval,新的zval的refcount为1,值为字符串"new string",大概过程如下:

$a = "Hello world"   //a: (refcount=1, is_ref=0)="Hello world"

$b = $a                    //a,b: (refcount=2, is_ref=0)="Hello world"

$b = "new string"    //a: (refcount=1, is_ref=0)="Hello world"   b: (refcount=1, is_ref=0)="new string"(发生分离操作)

 

这个分离逻辑可以表叙为:

对一个一般变量a(isref=0)进行一般赋值操作,如果a所指向的zval的计数refcount大于1,那么需要为a重新分配一个新的zval,并且把之前的zval的计数refcount减少1。

 

以上为普通赋值的情况,如果是引用赋值,我们看看这个变化过程:

$a = "Hello world"   //a: (refcount=1, is_ref=0)="Hello world"

$b = &$a                 //a,b: (refcount=2, is_ref=1)="Hello world"

$b = "new string"    //a,b: (refcount=2, is_ref=1)="new string"

 

可以看出来,对一个引用类型的zval进行赋值是不会进行分离操作的,实际上我们再产生一个引用变量的时候是可能出现一个分离操作的,只是时机有些不同:

1.在普通赋值的情况下,分离操作发生在$b="new string"这一步,也就是在对变量赋新的值的时候,才会进行zval分离操作

2.在引用赋值的情况下,分离操作有可能发生在$b = &$a这一步,也就是在生成引用变量的时候

 

情况1就不多解释了,情况2中强调是有可能发生分离,以前面的这代码为例子,是否进行分离与$a当前指向的zval的refcount有关系,代码中$b = &$a 的时候, $a指向的zval的refcount=1,这个时候不需要进行分离操作,但是如果refcount=2,那么就需要分离一个zval出来。比如如下代码:

 

在执行引用赋值的时候,$a指向的zval的refcount=2,因为$a和$c同时指向了这个zval,所以在$b=&$a的时候,就需要进行一个分离操作,这个分离操作生成了一个ref=1的zval,并且计数为2,因为$a,$b两个变量指向分离出来的zval,原来的zval的refcount减少1,所以最终只有$c指向一个值为"Hello world",ref=0的zval1, $a和$b指向一个值为"Hello world",ref=1的zval2。 这样我们对$c的修改时在操作zval1,对$a和$b的修改都是在操作zval2,这样就符合引用的特性了。

 

此过程大致如下:

$a = "Hello world";  //a: (refcount=1, is_ref=0)="Hello world"

$c  = $a;                  // a,c: (refcount=2, is_ref=0)="Hello world"

$b = &$a;                // c: (refcount=1, is_ref=0)="Hello world" a,b: (refcount=2, is_ref=1)="Hello world" (发生分离操作)

$b = "new string";   // c: (refcount=1, is_ref=0)="Hello world" a,b: (refcount=2, is_ref=1)="new string"

 

试想一下如果不进行这个分离会有什么后果?

如果不进行分离,$a,$b,$c都指向了同一个zval,对$b的修改也会影响到$c,这显然是不符合PHP语言特性的。

 

这个分离逻辑可以表述为:

将一个一般变量a(isref=0)的引用赋给另外一个变量b的时候,如果a的refcount大于1,那么需要对a进行一次分离操作,分离之后的zval的isref等于1,refcount等于2

 

 

 

通过以上的一些知识和分离逻辑读者应该可以很容易分析其它的一些情况:

比如:

将一个引用变量a(isref=1)的引用赋给一般变量b的时候,需要将b之前指向的zval的refcount减少1,然后将b指向a的zval,a的zval的refcount加1,没有任何分离操作

 

这些理论结合实际代码会让你更容易理解这个过程。

 

unset的作用

 unset()并非一个函数,而是一种语言结构,这个可以通过查看编译生成的opcode看到区别,unset对应的不是一个函数调用的opcode。那么unset到底做了什么? 在unset对应的opcode的handler中可以看到相关内容,主要的操作时从当前符号表中删除参数中的符号,比如在全局代码中执行unset($a),那么将会在全局符号表中删除a这个符号。全局符号表是一张哈希表,建立这张表的时候会提供一个表中的项的析构函数,当我们从符号表中删除a的时候,会对符号a指向的项(这里是zval的指针)调用这个析构函数,这个析构函数的主要功能是将a对应的zval的refcount减1,如果refcount变成了0,那么释放这个zval。所以当我们调用unset的时候,不一定能释放变量所占的内存空间,只有当这个变量对应的zval没有别的变量指向它的时候,才会释放掉zval,否则只是对refcount进行减1操作。

 

JavaScript中的垃圾收集机制(标记清除和引用计数) 垃圾收集机制 JavaScript 具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。这样我们在编写 JavaScript 代码时就不需在关心内存使用问题了,所需内存分配和已用内存的回收完全实现了自动管理。 这种垃圾收集机制的原理其实非常简单: 找出那些不再使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性的执行这... 阅读详情

相关推荐

php计数器,数据存于txt文本。

php计数器,数据存于txt文本,会自动在该文件所存目录生成counter.txt文件,适合PHP新手学习,了解文件操作函数的应用!

详谈PHP垃圾回收机制

这篇文章是在研究了PHP垃圾回收机制时,发现了PHP5和PHP7在垃圾回收上的一些区别,参考一些资料,记录一下。 参考文章如下 http://php.net/manual/zh/features.gc.php http://www.jb51.net/article/76732.htm

wangshiqueque的专栏 8827

IC617多线程仿真卡死?手把手教你用TigerVNC替换解决ADE XL Explorer问题(附Manjaro/Centos8实测)

本文针对IC617在Manjaro、CentOS 8等高版本Linux系统中运行ADE XL Explorer进行多线程仿真时出现的卡死问题,提供了详细的诊断与解决方案。文章指出问题根源在于软件内置的旧版VNC组件不兼容,并手把手指导用户通过使用TigerVNC替换原组件来彻底解决,附有在Manjaro和CentOS 8系统上的实测步骤与验证方法。

hill8的博客 774

php的垃圾回收机制——引用计数

每个php变量存在一个叫"zval"的变量容器中。一个zval变量容器,除了包含变量的类型和值,还包括两个字节的额外信息。第一个是"is_ref",是个bool值,用来标识这个变量是否是属于引用集合(reference set)。通过这个字节,php引擎才能把普通变量和引用变量区分开来,由于php允许用户通过使用&来使用自定义引用,zval变量容器...

weixin_34126215的博客 275

UE5.3像素流送Docker化部署:从Linux服务器搭建到云端实时渲染

本文详细介绍了在Linux服务器上使用Docker容器化部署UE5.3像素流送服务的完整流程。通过将虚幻引擎运行时、信令服务器和TURN服务打包为标准化镜像,利用Docker Compose进行编排,实现了云端实时渲染应用的快速部署、环境一致与弹性扩展,有效解决了传统部署中的环境依赖与运维难题。

k0l1m2n3o的博客 200

沧小海深入剖析xilinx的GTP/GTX核,掌握高速串行收发机制——第六章 接收端结构及功能说明

总目录在这哦~ https://blog.youkuaiyun.com/z123canghai/article/details/107700878 第六章 接收端结构及功能说明 子目录 6.1 RX的模拟前端 6.2 RX的OOB信号 6.3 RX的均衡器 6.4 RX的CDR 时钟数据恢复 6.5 RX时钟输出结构 6.6 RX 的极性控制 6.7 RX 的PRBS 6.8 Rx字节、字的对齐功能 6.9 Rx接收弹性缓冲区 6.10 Rx的时钟纠正 6.11 Rx接收通道绑定 6...

滄海的博客 1万+

【JVM】垃圾回收机制(GC)之引用计数和可达性分析

这种思想方法,并没有在JVM中使用,但是广泛应用于其他主流语言的垃圾回收机制中(PythonPHP《深入理解 Java 虚拟机》中谈到了引用计数,就导致有些面试官还是会问给每个对象安排一个额外的空间,空间里要保存当前这个对象有几个引用new1banulla1bnullb0此时垃圾回收机制发现对象的引用计数为0,说明这个对象就可以释放掉了0。

Yeeear的博客 1万+

Unity 升级引擎版本后构建报错:script class layout is incompatible between the editor and the player

Unity升级引擎版本后,项目报错:script class layout is incompatible between the editor and the player。

学习与分享 593

八十九、PHP内核探索:变量的引用与计数规则 ☞ 引用、计数与写时复制

变量的内部引用和计数 在引擎内部,一个PHP的变量是保存在“zval”结构中,此结构包含了变量的类型和值信息,这个在之前的文章变量的内部存储:值和类型中已经介绍了,此结构还有另外两个字段信息,一个是"is_ref"(此字段在5.3.2版本中是is_ref__gc),此字段是一个布尔值,用来标识变量是否是一个引用,通过这个字段,PHP引擎能够区分一般的变量和引用变量。PHP代码中可以通过 &a...

进阶之旅 343

保姆级教程:在Windows上用Docker Desktop一键部署Dify 1.0.1(附国内镜像加速)

本文提供在Windows系统上使用Docker Desktop快速部署Dify 1.0.1的详细教程,包括环境准备、国内镜像加速配置、Dify容器部署实战及常见问题解决方案。特别针对国内用户优化,确保快速完成部署并提升开发效率。

weixin_33738555的博客 282

Python+Vue计算机毕业设计风电场监控系统的设计与实现7bd6i(程序+LW+源码+部署)

Python3.7.7+Django+Mysql5.7+pip list+HBuilderX(Vscode也行)+Vue+Pychram社区版。2. 前端:vue+css+javascript+jQuery+easyUI+highcharts。2.IDE环境:Python和Python 社区版都可以。Django + Vue +Python+Mysql 等等组成,B/S模式等等。该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。1. 后端:Python、Vue、Django、Mysql。

华信计算机毕设源码程序 342

python 引用计数问题

文章目录对象的引用计数+1的情况:对象被创建对象被引用对象被作为参数,传入到一个函数中对象作为一个元素,存储在容器中对象的引用计数-1的情况:对象的别名被赋予新的对象对象的别名被显式销毁一个对象离开它的作用域对象所在的容器被销毁,或从容器中删除对象 python的垃圾回收采用的是引用计数机制为主和分代回收机制为辅的结合机制,当对象的引用计数变为0时,对象将被销毁,除了解释器默认创建的对象外。(默认...

记录求学阶段的技术学习、项目实践与成长思考。 3461

【导航理论&实战篇】四足机器人弹簧倒立摆(SLIP)动力学模型

认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长!本文先对四足机器人动力学模型-VMC、SLIP和浮动机体模型做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章四足机器人弹簧加载倒立摆(SLIP)动力学模型是一种用于描述和分析四足机器人在运动过程中动态特性的理论模型。这种模型将四足机器人的腿部简化为弹簧,而机器人的身体则被视为一个倒立摆,这样的抽象使得研究者能够更容易地分析和控制机器人的运动。提示:以下是本篇文章正文内容。

qq_35635374的博客 806

PHP基础终极版-面试大全 (一) 深度理解变量的传值和引用

本文基础学习内容: 如何深度全面的理解PHP中的变量、及变量的传值和引用知识点,让你在工作中快速上手,成为技术强人,在面试中快速超越他人拿到offer。

huidaoli 2699

YOLO26改进策略【Neck】| CVPR 2024 MFM(Modulation Fusion Module,调制融合模块):动态特征加权融合,突出关键特征抑制冗余

本文主要利用DCMPNet中的 MFM 模块优化 YOLO26 的目标检测网络模型。模块通过动态调制特征融合过程,实现了对多尺度、跨层级特征的智能聚合。将其应用于的改进过程中,针对目标检测中边界特征与语义信息的互补性需求,缓解网络中浅层细节与深层语义融合不足的问题。Depth Information Assisted Collaborative Mutual Promotion Network for Single Image Dehazing在图像去雾网络中,不同层级和类型的特征包含着互补的信息(如浅层的

Limiiiing的博客 223

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

php 查看变量的引用数,php变量的引用与计数规则

变量的内部引用和计数在引擎内部,一个PHP的变量是保存在“zval”结构中,此结构包含了变量的类型和值信息,这个在之前的文章变量的内部存储:值和类型中已经介绍了,此结构还有另外两个字段信息,一个是"is_ref"(此字段在5.3.2版本中是is_ref__gc),此字段是一个布尔值,用来标识变量是否是一个引用,通过这个字段,PHP引擎能够区分一般的变量和引用变量。PHP代码中可以通过 &amp...

weixin_34221384的博客 263

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

python计数变量_Python 对象的引用计数和拷贝

Python 对象的引用计数和拷贝Python是一种面向对象的语言,包括变量、函数、类、模块等等一切皆对象。在python中,每个对象有以下三个属性:1、id,每个对象都有一个唯一的身份标识自己,可通过内建函数id(obj)查看。2、type,对象的类型决定了该对象可以保存什么类型的值,可用内建函数type(obj)查看;3、value,即对象的值。下面是一个例子:>>> str...

weixin_39875754的博客 319

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Java——引用变量

内存是一段连续的存储空间,主要用来存储程序运行时数据的。JVM也对所使用的内存按照功能的不同进行了划分。

qq_45559559的博客 887

PHP底层那些事--生命周期、变量zval、引用计数

从内核层面解析PHP的声明周期、变量zval、引用计数。 《Extending and Embedding PHP》读后总结 1.PHP的生命周期 不论是使用cli指令行还是使用webserver(apache、nginx)形式解释执行php程序。最终的进程模型都可以归为三类: 单进程(cli):指令行执行php就是开启一个php进程,执行完php程序后控制权返回给shell。 多进程(nginx+php-fpm):php-fpm实际处理php请求的只是worker进程,在单个进程空间中并不会创建线程.

Orz..一入码门深似海 514

89-变量的引用与计数规则

89-变量的引用与计数规则变量的内部引用和计数在引擎内部,一个PHP的变量是保存在“zval”结构中,此结构包含了变量的类型和值信息,这个在之前的文章 变量的内部存储:值和类型 中已经介绍了,此结构还有另外两个字段信息,一个是”is_ref”(此字段在5.3.2版本中是is_ref__gc),此字段是一个布尔值,用来标识变量是否是一个引用,通过这个字段,PHP引擎能够区分一般的变量和引用变量。PHP

phantom66的博客 1058

php应用变量怎么计算,php变量的引用与计数规则

变量的内部引用和计数在引擎内部,一个PHP的变量是保存在“zval”结构中,此结构包含了变量的类型和值信息,这个在之前的文章 变量的内部存储:值和类型 中已经介绍了,此结构还有另外两个字段信息,一个是"is_ref"(此字段在5.3.2版本中是is_ref__gc),此字段是一个布尔值,用来标识变量是否是一个引用,通过这个字段,PHP引擎能够区分一般的变量和引用变量。PHP代码中可以通过 &amp...

weixin_39616686的博客 184

javascript的变量存储机制和原理

js的变量存储机制

honeymoon_的博客 1022

python实例化对象计数_python – 了解类变量的引用计数

这是为了更好地理解引用计数在Python中的工作原理.让我们创建一个类并实例化它.实例的引用计数为1(getrefcount显示2,因为它自己的内部结构引用该类实例将引用计数增加1):>>> from sys import getrefcount as grc>>> class A():def __init__(self):self.x = 100000>>> a = A()>>> grc(a)2...

weixin_39758032的博客 231
上一篇: zend_execute: op执行过程详解
下一篇: PHP新的垃圾回收机制:Zend GC详解
phpkernel
博客等级 码龄16年 96粉丝 13原创
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值