JVM的垃圾回收机制和Golang的垃圾回收机制

本文探讨了JVM和Golang的垃圾回收机制,包括可达性分析算法、引用计数算法、标记-清除、复制、分代收集等JVM算法,以及Golang的mark and sweep算法。分析了两种语言在垃圾回收中的相似点和不同点,以及各自的发展趋势。

JVM的垃圾回收机制和Golang的垃圾回收机制


前言

为什么要写这篇文章,在仔细的了解了jvm垃圾回收机制和Golang的回收机制之后发现其有很多的相同点和不同点,未来的发展会不会有共通之处,这是我的想法。


一、 哪些内存需要回收

在弄清答案之前我们首先要知道什么样数据我们是可以回收的。

JVM的内存结构包括五大区域:程序计数器、虚拟机栈、本地方法栈、堆区、方法区。
其中程序计数器、虚拟机栈、本地方法栈3个区域随线程而生、随线程而灭,因此这几个区域的内存分配和回收都具备确定性,就不需要过多考虑回收的问题
Java堆区和方法区则不一样,内存的分配和回收是动态的所以针对这部分区域有一些特定的算法。

二、算法

2.1可达性分析算法

在这里插入图片描述
简单的说法就是看其是否于GCRoot相连,如果不相连我们认为其可回收。
在Java语言中,可作为GC Roots的对象包括下面几种:
1、虚拟机栈中引用的对象(栈帧中的本地变量表);
2、方法区中类静态属性引用的对象;
3、 方法区中常量引用的对象;
4、本地方法栈中JNI(Native方法)引用的对象。
(1)我们可以思考一个问题,那就是会不会出现循环引用的问题?
不会,我们看可达性分析算法的结构,其值是向下进行搜索的不会得到闭环。
(2)不被对象图内对象引用的会不会被直接回收?
不会的,在这个算法中有一个标记的过程,只有当对象被标记了两次才会被回收。筛选的条件是此对象是否有必要执行finapze()方法。当对象没有覆盖finapze()方法,或者finapze()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。程序中可以通过覆盖finapze()来一场"惊心动魄"的自我拯救过程,

2.2引用计数算法

这个方法相对而言比较简单。堆中的每一个对象实例我都对其count赋值为1,一旦我使用他我就将其+1;当我每过一个生命周期时就减1,当count == 0 时我们就认为其可以进行GC回收。
优点:在长时间的运行程序中比较有利
缺点:无法进行循环检测。

三.算法

3.1引用计数算法

方式与2.2一致不赘述了

3.2 标记-清除算法

在这里插入图片描述就是标记需要回收的内存,之后进行清除。图中可以清除的看到很多未使用的内存零散的分布在内存空间中,会造成内存碎片。

3.3 复制算法

在这里插入图片描述将内存分成两块,当一块内存快使用完了,进行垃圾回收,将存活的对象复制到另一半内存,这样我们就可以对那一半内存进行清空,不会产生内存碎片。

3.4 分代收集算法

将内存划分为三个部分:新生代、老年代、永久代(8:1:1)。
在这里插入图片描述在这里插入图片描述在这里插入图片描述
从上面三图可以很轻松的看出,当我们的到一个新对象的时候我们会将其放进Eden中,每当我们调用第一次的时候会将其放入S0中,再次调用会将其放入S1中;反复调用15轮的时候我们会将其加入到老年代中。老年代中还可以加入一些首次声明的大对象(比如长字符串/大数组),新生代中的剩余空间不足的时候,这些大对象会直接被分配到老年代中。

4. Go的垃圾回收机制

采用的是经典的mark and sweep算法。

1.3版本以前,golang的垃圾回收算法都非常简陋:go runtime在一定条件下(内存超过阈值或定期如2min),暂停所有任务的执行,进行mark&sweep操作,操作完成后启动所有任务的执行。在内存使用较多的场景下,go程序在进行垃圾回收时会发生非常明显的卡顿现象(Stop The World)。在对响应速度要求较高的后台服务进程中,解决这个问题比较常用的方法是尽快控制自动分配内存的内存数量以减少gc负荷,同时采用手动管理内存的方法处理需要大量及高频分配内存的场景。
1.3版本中,go runtime分离了mark和sweep操作,和以前一样,也是先暂停所有任务执行并启动mark,mark完成后马上就重新启动被暂停的任务了,而是让sweep任务和普通协程任务一样并行的和其他任务一起执行。如果运行在多核处理器上,go会试图将gc任务放到单独的核心上运行而尽量不影响业务代码的执行。减少了50%-70%的暂停时间。
1.4版本(当前最新稳定版本)对gc的性能改动并不多。1.4版本中runtime很多代码取代了原生c语言实现而采用了go语言实现,对gc带来的一大改变是可以是实现精确的gc。c语言实现在gc时无法获取到内存的对象信息,因此无法准确区分普通变量和指针,只能将普通变量当做指针,如果碰巧这个普通变量指向的空间有其他对象,那这个对象就不会被回收。而go语言实现是完全知道对象的类型信息,在标记时只会遍历指针指向的对象,这样就避免了C实现时的堆内存浪费(解决约10-30%)。
1.5版本go team对gc又进行了比较大的改进(1.4中已经埋下伏笔如write barrier的引入),官方的主要目标是减少延迟。go 1.5正在实现的垃圾回收器是“非分代的、非移动的、并发的、三色的标记清除垃圾收集器”。分代算法上文已经提及,是一种比较好的垃圾回收管理策略,然1.5版本中并未考虑实现;我猜测的原因是步子不能迈太大,得逐步改进,


总结

简述GC回收的是那部分数据、GC回收机制的几种算法、当前hotspot的主要使用的GC回收方式、Golang的发展方式。

参考文献

https://blog.csdn.net/u010649766/article/details/80582153
https://www.cnblogs.com/wabi87547568/p/5282892.html
https://mp.weixin.qq.com/s/pG1-UtZCHztuMzKFV-gOgQ
https://blog.csdn.net/aijiudu/article/details/72991993

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值