简述Java的几种GC算法

一、引用计数法与增量收集法

· 引用计数法

古老的回收算法
原理:通过记录并操作每个对象被引用的次数而控制GC
说明:
引用一次,次数加一。当对象的生命周期结束时,引用减一。当对象出了作用域后(该对象丢弃不再使用计数器减1)。此算法现已不再使用

缺点:无法循环处理引用

· 增量收集法

增量收集法(Incremental Collecting)。增量收集法是在应用,运行的时候运行算法进行垃圾回收,但是在JDK5以后不再使用该算法。


二、⚫ 标记-清除

实现过程

第一阶段:标记。从引用根节点开始标记所有有被引用的对象

这一阶段——标记,也就告诉我们,它将会遍历属于Java的内存空间

第二阶段:清除。遍历整个堆,把未被标记的对象清除。

GC执行了这个算法之后,你以为它内存就回收完毕了?是回收完了,但是内存空间缺因这个而产生空间碎片,如下图。

简述Java的几种GC算法_第1张图片

说明:

在垃圾回收算法中,根是指向对象的指针的起点部分。
通过根对象进行引用搜索,最终可达的对象被称为可达对象(被引用对象);
通过根对象进行引用搜索,最终没有被引用的对象被称为不可达对象(即垃圾对象(不要理解歪咯哈哈哈))。


原因:对象占用内存空间不同而导致对象回收后空间呈碎片化。 你的对象所占用的内存大小又得根据你定义的对象具体内容来决定,因此这些碎片空间你是不能够利用到他们的特别是在进行大对象内存分配时,相较于连续内存空间,内存碎片的存在会降低堆内存效率因为Java内存空间的使用,并不一定是连续的,而且假如是连续使用了的空间,但在回收一部分未被(不可达)对象之后,他依然会产生这种空间碎片,这种空间碎片大小不一致,位置错乱分布。
打个比方:你的行李箱里面,如果你能规划好它的空间、安排好怎么放东西,便能使得空间利用率达到你所需要的最高,但是当你一股脑的往里面塞东西,往往它的容纳量是不尽你意的。
简述例子:划分好李箱空间,需要放的东西进行分类,以达到空间最大利用率。


优化与改进(转载自: 知乎:GC算法之一 标记-清除算法)

多空闲链表
简述Java的几种GC算法_第2张图片

其实产生内存碎片和分配速度慢的主要原因是因为我们的只用到一条空闲链表的缘故。如果我们对块的大小进行分类:1字节的块放在 free_list_1, 2字节的块在free_list_2,… 直到free_list_100,对于100或100字节以上的块我们统一放到free_list_100里面。这样的话分配对象内存时就不用遍历整个链表而是根据大小到对应的具体的空闲链表,这样的时间会更快。并且因为作为大小区分,很多对象都能找到合适的块,有效的减少和避免了内存碎片的产生。这样的话内存碎片和分配速度慢的问题都可以得有效解决和缓解。


三、⚫ 复制-清除

实现:
① 把内存空间划分为两个相等的区域,每次只使用其中一个区域
② GC时,遍历当前使用区域,将正在使用的对象复制到另一个区域中去并加以整理
③ 清除正在使用区的所有对象
④ 交换两个内存区


① 把内存空间划分为两个相等的区域,每次只使用其中一个区域
简述Java的几种GC算法_第3张图片

② GC时,遍历当前使用区域,将正在使用的对象复制到另一个区域中去并加以整理
简述Java的几种GC算法_第4张图片

③ 清除正在使用区的所有对象
简述Java的几种GC算法_第5张图片

④ 交换两个内存区
简述Java的几种GC算法_第6张图片


结果分析:

我们看图可以发现,尽管复制清除算法有效的处理空间碎片产生的问题,但是却导致有一半的内存空间是我们不能使用的。我感觉这里就好像是所谓“鱼和熊掌不可兼得”??
缺点:内存空间损失一半

详细分析见:知乎:GC算法之二 复制-清除算法)

四、⚫ 标记-压缩

第一阶段:标记-清除
第二阶段:压缩


标记-压缩算法融合了“标记-清除”与“复制-清除”两种算法的优点,不产生空间碎片,同时能够不占用多余的内存空间
实现过程:

简述Java的几种GC算法_第7张图片
标记压缩法是一种针对老年代的垃圾回收算法。(有关年老代,我会再后续的博客中,想最简单的方式对那一类概念做描述并作图举例)对于老年代,由于大部分对象都是存活对象,使用复制算法代价将使内存折半,成本太高并不适用。标记压缩法正是为了应对这种情况而产生,首先从根节点开始,对所有可达对象进行标记,然后将所有存活的对象压缩到内存一端,再清理剩余的所有的空间。这种方法既避免了碎片的产生,又不需要将内存分为两半,效率很高。

详细分析见:知乎:GC算法之三 标记-压缩算法)

五、⚫ 分区算法(这里有必要清楚JVM内存的分代所以我后面)(等明白JVM内存划分之后,会考虑将GC部分与JVM内存融合一起来分析)

分区算法将整个堆空间划分成连续的不同小区间,每一小区间
都独立使用,独立回收,并可以控制一次回收的小区间数量。一般
来说,在相同条件下,堆空间越大,进行一次垃圾回收的所需的时
间就越长,导致产生的停顿时间也越长。因此如果将堆内存分割成
多个小块,并根据目标停顿时间的要求,每次合理地控制回收部分
小区间,而不是回收整个堆空间,则可以有效减小一次垃圾回收所
产生的的停顿时间。


参考文献与部分知识来源:
[1] 周志明 . 深入理解 Java 虚拟机 :JVM 高级特性与最佳实践 [M].
机械工业出版社 ,2012.
[2] 乔鑫 , 李志远 , 程建军 , Java垃圾回收方式性能研究.浪潮电子信息产业股份有限公司.电子技术与软件工程.电子技术与软件工程. 2020,(11)
[3]知乎博主jvm专栏:链接

以上个人在学习过程中的总结,有问题的地方希望各位指正,一起学习,共同进步!

如有侵权,联系删除

你可能感兴趣的:(java,jvm)