如何在JVM中基于引用计数法实现GC

一. 为什么使用RC

       再Java虚拟机中,目前广泛使用的是引用计数法,具体详细请见:说说JVM的垃圾回收机制-CSDN博客 而为什么我使用引用计数来实现gc呢?其一是因为可达性分析法中我们需要先收集根集对象,GC roots主要包括:

  1. 再虚拟机栈中引用的对象,即栈帧本地方法表中引用的对象变量
  2. 方法区中静态属性引用的对象
  3. 方法区中常量引用的对象
  4. JNI中引用的对象,即本地方法引用的对象
  5. JVM内部引用,包括基本数据类型对象的Class对象,类加载器对象,异常对象等等。
  6. 所有被同步锁持有的对象。

所以根集的收集是十分麻烦的。同时由于可达性分析法实际上是一种标记算法,即会标注每个对象是否死亡。而后续的内存释放一系列工作则需要使用抽象的分区概念(青年代,老年代,其他书也有伊甸园代等等对分区的称谓)使用标记-清除,复制-清除,标记整理法来进行处理。所以整体实现比较麻烦,然后也会存在 stop the world 来进行暂停gc,此时需要涉及到gc线程,而我的虚拟机并未实现多线程,所以这是其二原由。然后引用计数法相较而言就比较简单了,它是在运行时进行gc的一种方法,即不存在 stop the world 虽然会带来一些内存开销,并且只需要控制一些指令对计数进行增减就可以实现判断该对象是否需要被gc,当计数为0直接释放该对象的内存。当然为什么目前主流JVM不使用引用计数是因为其存在一个较大的问题:循环引用!

二. 说说引用计数发的循环引用缺陷

循环引用是指一组对象互相引用,形成一个闭环,即使这组对象已经不再被外部引用,它们的引用计数也不会降至0,因此它们的内存不会被回收,从而导致内存泄漏(也就是我们所说的漂浮垃圾)。

public class Node {public Node other;
}public class Main {public static void main(String[] args) {Node A = new Node();Node B = new Node();A.other = B;B.other = A;// 将外部引用设为nullA = null;B = null;}
}

循环引用有解决方案吗?当然有!

目前python的gc就是使用的引用计数法,python的解决方案是引入一个周期性运行的循环检测器来处理循环引用(详情见:python虚拟机的GC算法使用引用计数是如何处理循环引用的? - 知乎 (zhihu.com) )

当然本文不深入讨论循环检测器,接下来来详细说说引用计数法。

三. 引用计数的实现

给对象添加一个引用计数器,每当有一个地方引用它时,计数器就加一;当引用失效时,计数器就减一。当计数器为0时,说明对象没有任何引用,即为垃圾对象,可以被回收。代码实现如下:

// AddRef 增加对象的引用计数
func (self *Object) AddRef() {self.count++//fmt.Printf("Object name: %v,\tcount: %v\n", self.class.name, self.count)
}// Release 减少对象的引用计数,并在计数为0时释放对象
func (self *Object) Release() {self.count--if self.count == 0 {self.Free()}
}// Free 释放对象占用的资源
func (self *Object) Free() {if slots, ok := self.data.(Slots); ok {for _, slot := range slots {if slot.ref != nil {slot.ref.Release()}}}// Clear other references to allow GC to reclaim memoryself.class = nilself.data = nilself.extra = nil
}

然后我们初始化的时候有一个问题需要说明下,其实再对象创建的时候他的引用计数为0,例如:

Class A{
}
new A();

此时该对象未赋给任何值,所以计数为0,只有当它被赋给某个变量时,才会计数++,(数组类对象除外,在java编译器对这里有严苛的语法要求,数组类对象创建后必须复制给某个变量)。我为了便于操作,且一般创建对象我们都会赋给某个变量,所以我就直接初始化为1了。接下来就是讨论计数加减的时候,具体参考jvm规范:

计数++:

  1. 当对象被创建并有变量引用它时:例如,Object obj = new Object();在这里,新创建的对象被obj变量引用,所以引用计数加一。

  2. 当对象被另一个对象引用时:例如,Object obj2 = obj;在这里,obj2变量也引用了obj所指向的对象,所以引用计数再次加一。

  3. 当对象被作为方法参数传递时:例如,在调用someMethod(obj);时,obj被作为参数传递给了方法,所以引用计数加一。

  4. 当对象被作为方法返回值返回时:例如,在return obj;中,obj被作为返回值返回,所以引用计数加一。

计数--:

  1. 当一个对象的引用被赋值为null时:例如,Object obj = new Object(); obj = null;。在这种情况下,obj不再引用先前创建的对象,因此该对象的引用计数减一。

  2. 当对象的引用超出其作用域时:例如,如果一个对象只在一个方法内部被引用,那么当该方法执行完毕后,该对象的引用计数应该减一。

  3. 当一个对象被另一个对象的字段引用,然后这个字段被赋值为另一个对象或null时:例如,obj1.field = obj2; obj1.field = null;。在这种情况下,obj2的引用计数减一。

  4. 当一个对象作为方法参数,方法执行结束后:例如,void someMethod(Object param) { ... },在方法执行结束后,param的引用计数减一。

落实到指令上,无非就是如下几个:

  1. newanewarraymultianewarray :会创建一个新的对象,并将引用计数设置为1
  2. astoreaastoredup : 会增加一个新的引用,所以引用计数需要增加。
  3. aconst_null、astoreaastore原来的引用被消除,所以原来引用的计数需要减少。
  4. invokevirtualinvokeinterfaceinvokespecialinvokestatic :调用一个方法时,会将对象引用传递给方法作为参数,这会增加一个新的引用。当方法返回时,如果返回值是一个对象引用,那么这个引用会被复制到调用者的操作数栈中,这也会增加一个新的引用。相反,如果方法返回后,原来的参数引用不再被使用,那么这些引用需要被消除,所以引用计数需要减少。
  5. 异常处理,未实现暂不考虑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/23216.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

idea 常用插件推荐

文章目录 1、Lombok2、Convert YAML and Properties File3、Grep Console4、MyBatisX5、Free MyBatis Tool6、MyBatis Log EasyPlus (SQL拼接)7、MyBatisPlus8、Eclipse theme9、Eclipse Plus Theme10、Rainbow Brackets Lite - Free and OpenSource&…

操作系统教材第6版——个人笔记3

2.1 处理器 2.1.1 处理器与寄存器 处理器部件的简单示意 用户程序可见寄存器 可以使程序员减少访问主存储器的次数,提高指令执行的效率所有程序可使用,包括应用程序和系统程序数据寄存器:又称通用寄存器地址寄存器:索引、栈指针…

Linux基础知识(十二)

1.新建用户 1)useradd 2)passwd 2.完成的操作 1)/etc/passwd添加一行 2)/etc/shadow添加一行 3)/etc/group添加一行 4)创建家目录 5)创建用户邮件文件 例:创建用户jerry 要求:UID:777 主…

review of c++

友元关系是单向的。 指针

为什么要做与运算?网关如何和ip做与运算?

在计算机网络中,“与运算”是一个基本而重要的概念,尤其在IP地址和子网掩码的处理中起着关键作用。本文将解释为什么要进行与运算,以及网关如何和IP地址进行与运算。 为什么要做与运算? 1. 确定网络地址 与运算(AND…

Linux学习笔记9

Linux 进程间通信 介绍一下管道,管道是一种特殊的文件,它通过文件描述符来进行访问和操作 管道的读写操作是阻塞式的,如果没有数据可读,读操作会被阻塞,直到有数据可读;如果管道已满,写操作也…

CodeArts 6月体验官活动重磅来袭,限量领取华为GT系列手表!

千呼万唤始出来,CodeArts 6月体验官活动来啦! 为了让开发者更好地体验CodeArts,小编特意给大家准备了重磅好礼。 不仅有华为GT系列手表,还有开发者定制礼盒,更有多重定制好礼~ 快叫上小伙伴一起来体验CodeArts&…

2024年湖北职称评审面试答辩技巧有哪些?看完你就懂了

2024年度湖北省部分工程专业水平能力测试面试答辩开始了,答辩时间是:2024年6月15、16日。 测试地点:武汉市武昌区洪山侧路63号茶港军转小区1号楼(武汉大学西门旁) 水平能力测试注意事项: (一)报名参加202…

数据结构之归并排序算法【图文详解】

P. S.:以下代码均在VS2019环境下测试,不代表所有编译器均可通过。 P. S.:测试代码均未展示头文件stdio.h的声明,使用时请自行添加。 博主主页:LiUEEEEE                        …

mediaPlayer的内存泄露解决方法

MediaPlayer在Android中用于播放音频和视频。如果不正确管理,MediaPlayer可能会导致内存泄漏,尤其是当它被用于多个Activity或长时间播放时。以下是一些解决MediaPlayer内存泄漏的方法: ### 1. 及时释放资源 当MediaPlayer不再使用时&#x…

Kolmogorov–Arnold Networks (KAN) 即将改变 AI 世界

目录 一、说明 二、KAN介绍 2.1 什么是 Kolmogorov-Arnold Networks (KAN): 2.2 KAN 的秘诀,Splines! 2.3 了解KAN工作的最简单方法 三、KAN的主要优点 四、KAN 的 Python 实现 (PyKAN) 4.1 …

可燃气体报警器效检:预防事故,守护家园

在现代化工业生产、居民生活中,可燃气体报警器作为安全预防的重要工具,其准确性和可靠性直接关系到人们的生命财产安全。 因此,对可燃气体报警器进行定期效检,确保其处于最佳工作状态,是保障安全生产的必要措施。 接…

Java集合之List(超详细)

List是Java集合框架中一个非常重要的接口,它代表了一个有序的集合,允许元素重复,并且可以按照插入的顺序进行访问。 我们先来看看List在集合中的位置: List是单列集合接口Collection下的一个分支,另两个分支是Set和Qu…

【Redis数据库百万字详解】数据类型

文章目录 一、字符串类型概述1.1、数据类型1.2、字符串简介1.3、字符串应用场景 二、字符串命令三、哈希类型概述3.1、哈希介绍3.2、哈希类型应用场景3.3、哈希命令 四、列表类型概述4.1、列表简介4.2、使用场景4.3、列表命令 五、集合概述5.1、集合简介5.2、使用场景5.3、集合…

[大师C语言(第二十一篇)]C语言字节对齐技术详解

引言 在计算机系统中,内存对齐是一种非常重要的技术。它指的是数据在内存中的存放位置与内存地址之间的关系。C语言作为一种高级编程语言,提供了丰富的内存对齐操作,使得程序员可以灵活地控制数据在内存中的布局。本文将深入探讨C语言对齐背…

JavaScript中,ToPrimitive的操作把对象转化为原始值

在JavaScript中,ToPrimitive是一个抽象操作,不是一个实际的方法。ToPrimitive操作用于将对象转换为原始值(例如,字符串、数字或布尔值)。这个操作通常在需要原始值的情况下自动执行,例如在比较或算术运算中…

网络工程从头做-1

网络工程从头做-1 自下而上,从接入交换机开始网络的配置和规划 实验拓扑: 实验步骤: 1.完成基本配置 1.1 PC端IP地址信息配置略 1.2 接入层交换机S1配置 [Huawei]sys S1 [S1]undo in [S1]vlan b 10 20 [S1]int e0/0/1 [S1-Ethernet0/0/1]p l…

k8s怎么监听自定义资源的变更?(2)

接上一篇当生成下面代码之后怎么去使用呢? 1.生成crd文件 这里我们通过kubebuilder的一个子项目 controller-gen 来生成crd文件 https://github.com/kubernetes-sigs/controller-tools curl -L -o https://github.com/kubernetes-sigs/controller-tools; go ins…

48、Flink 的 Data Source API 详解

a)概述 本节将描述 FLIP-27 中引入的新 Source API 的主要接口。 b)Source Source API 是一个工厂模式的接口,用于创建以下组件。 Split EnumeratorSource ReaderSplit SerializerEnumerator Checkpoint Serializer 此外,Sou…

D-Day 上海站回顾丨以科技赋能量化机构业务

5月31日下午,DolphinDB 携手光大证券,在上海成功举办 D-Day 行业交流会。三十余位来自私募机构的核心策略研发、量化交易员、数据分析专家们齐聚现场,深入交流量化投研交易过程中的经验、挑战及解决方案。 DolphinDB 赋能机构业务平台 来自光…