首页 > 编程语言 >JVM系列(九) -垃圾对象的回收算法介绍

JVM系列(九) -垃圾对象的回收算法介绍

时间:2024-10-17 14:46:12浏览次数:9  
标签:标记 对象 回收 算法 垃圾 JVM 内存

一、摘要

在之前的文章中,我们介绍了 JVM 内部布局、对象的创建过程以及运行期的相关优化手段。

今天通过这篇文章,我们一起来了解一下对象回收的判定方式以及垃圾对象的回收算法等相关知识。

二、对象回收判定方式

当一个对象被创建时,虚拟机会优先分配到堆空间中,当对象不再被使用了,虚拟机会对其进行回收处理,以便释放内存空间,这个过程也被称为垃圾对象回收。

那么如何找到对象是否可以进行回收呢?一般有两种方式。

  • 引用计数法
  • 可达性分析法

下面我们一起来了解下相关知识。

2.1、引用计数法

这个方法的实现思路是:在对象中维护一个引用计数器,每当一个地方引用这个对象时,计数器值+1;当引用失效时,计数器值-1。当对象的计数器值为 0,表示这个对象不再被使用了,可以被回收。

这种方法使用场景很多,但很少有垃圾收集器会使用这种方式。

原因在于:这种方式存在一个致命的缺陷,比如堆中的两个对象相互引用,此时他们的计数器值是 1,但这两个对象并没有被外部使用,因此不会被回收,容易造成内存泄露。

2.2、可达性分析法

这个方法的实现思路是:从“GC Roots”(这个 GC Roots 可以是栈中的引用变量,也可以是方法区的引用变量或常量)开始扫描堆中的对象,沿着 GC Roots 一路扫描,被扫描的所有对象全部标记为存活对象;扫描完成之后,没有被标记的视为垃圾对象,可以被回收。

比如对象 A 被线程占中的变量 a 引用着,对象 A 中引用着对象 B,对象 B 又引用着 C 等,沿着 a 开始扫描,会扫描到对象 A,B,C 等,并把它们标记为存活对象。全部扫描完成之后,当一个对象到 GC Roots 没有任何引用链时,表示此对象是不可用的,等待被 GC 回收。


在 JVM 中,可以作为 GC Roots 的对象包括:

  • 虚拟机栈中引用的对象
  • 方法区中静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中 JNI(即 Native 方法)引用的对象

三、垃圾回收算法

当一个对象被判定为垃圾对象之后,剩下的工作就是如何进行回收了。

下面我们一起来看看常见的几种垃圾回收算法的思想。

3.1、标记-清除算法

标记-清除算法如同它的名字一样,分为“标记”和“清除”两个阶段,也是最基础的算法。

首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其缺点进行改进而得到的。

这个算法也有很多的不足,主要体现在效率和空间。

  • 从效率的角度讲,标记和清除两个过程的效率都不高;
  • 从空间的角度讲,标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致后面的程序运行过程中分配较大对象时,无法找到足够的连续内存而不得不提前触发一次垃圾收集动作

标记-清除算法执行过程,可以用如下图来概括:

3.2、复制算法

复制算法是为了解决效率问题而出现的,它将可用的内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

这样每次只需要对整个半区进行内存回收,内存分配时也不需要考虑内存碎片等复杂情况,只需要移动指针,按照顺序分配即可。

这个算法也有缺点,操作的时候内存会缩小为了原来的一半,代价很高;其次,持续复制长生存期的对象会导致回收效果不佳,效率较低。

一般的商用虚拟机会采用这种算法来回收新生代(也称为年轻代)的对象,不过研究表明1:1的比例不是很科学,因此新生代的内存空间被细划分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor;每次回收时,将 Eden 和 Survivor 空间中还存活的对象一次性复制到另外一块 Survivor 空间上,最后清理掉之前的 Eden 和 Survivor 空间。

HotSpot 虚拟机默认 Eden 和 Survivor 区的比例是8 : 1 : 1,期望每次回收后只有不到 10% 的对象存活,如果出现 Survivor 空间不够用时,需要依赖老年代进行分配担保。

复制算法执行过程,可以用如下图来概括:

3.3、标记-压缩算法

在上面我们提到了复制算法的优点和缺点,针对对象存活率较高的场景,进行大量的复制操作时,效率很低下。如果不想浪费 50% 的空间,当对象 100% 存活时,那么需要有额外的空间进行分配担保。

在 HotSpot 虚拟机中,堆空间划分成两个不同的区域:新生代老年代,目的是为了更有效率的回收对象。新生代的对象存活率低,会优先被回收,如果多次执行依然没有被回收,就会转移到老年代。老年代都是不易被回收的对象,对象存活率高,因此一般不能直接选用复制算法。

根据老年代的特点,有人提出了另外一种标记-整理算法,也称为标记-压缩算法,过程与标记-清除算法一样,不过不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉边界外的内存。

标记-整理算法执行过程,可以用如下图来概括:

3.4、分代收集算法

分代收集算法,可以看成以上内容的延伸。它的实现思路是根据对象的生命周期的不同,将内存划分为几块,比如把堆空间划分为新生代老年代,然后根据各块的特点采用最适当的收集算法。

在新生代中,存在大批对象死去、少量对象存活的特点,会采用**“复制算法”,只需要付出少量存活对象的复制成本就可以完成垃圾对象收集,效率高;在老年代中,存在对象存活率高、没有额外空间对它进行分配担保的特点,会采用“标记-清理”或者“标记-整理”**算法来进行回收。

可以用如下图来概括堆内存的空间布局:

四、小结

最后总结一下,对象的回收判断方式,JVM 主要采用可达性分析法来处理。

至于对象的回收方式,会根据对象所在内存区域的不同,采用不同的垃圾收集算法来处理。对于年轻代的对象,主要采用复制算法;对于老年代的对象,标记-压缩算法应用比较广泛。

最近对 JVM 技术知识进行了重新整理,再次献上 JVM系列文章合集索引,感兴趣的小伙伴可以点击如下地址快速查阅。

五、参考

1.https://zhuanlan.zhihu.com/p/267223891

2.https://www.cnblogs.com/xrq730/p/4836700.html

3.https://zhuanlan.zhihu.com/p/248709769

4.http://www.ityouknow.com/jvm/2017/09/28/jvm-overview.html

写到最后

最后。如果感觉文章内容不错,帮忙动动小指头点个赞,点赞对我真的非常重要!加个关注我会非常感激!

标签:标记,对象,回收,算法,垃圾,JVM,内存
From: https://blog.csdn.net/dxflqm_pz/article/details/143005083

相关文章

  • 工装识别算法 工服穿戴检测系统
    工装识别算法工服穿戴检测系统特点包括:工装识别算法工服穿戴检测系统利用图像识别技术,系统可以准确地识别工人是否穿戴了正确的工装,包括工作服、安全帽等。一旦检测到未穿戴的情况,系统将立即发出警报,并提示相关人员进行整改。工装识别算法工服穿戴检测系统对于电力作业场景,系统......
  • JVM 实战篇(一万字)
    此笔记来至于黑马程序员内存调优内存溢出和内存泄漏内存泄漏(memoryleak):在Java中如果不再使用一个对象,但是该对象依然在GCROOT的引用链上,这个对象就不会被垃圾回收器回收,这种情况就称之为内存泄漏。内存泄漏绝大多数情况都是由堆内存泄漏引起的,所以后续没有特别说明......
  • 常见的缓存淘汰算法
    应用场景:缓存淘汰算法可以广泛应用于任何有缓存淘汰需求的场景,并不仅限于某个特定的插件或工具。许多软件和系统,如数据库(Redis、Memcached)、Web服务器(Nginx、Varnish)、内容分发网络(CDN)、浏览器缓存、甚至操作系统的内存管理,都会使用这些算法来决定在缓存空间满时该移除哪些数据......
  • 算法->二分查找
    二分查找找>=target的第一个位置找<=target的最后一个位置classSolution{public://找>=target的第一个位置intbinarySearchLeft(vector<int>&nums,inttarget){intleft=0,right=nums.size()-1;while(left<=right){......
  • 算法(第4版)练习题 3.3.20 的一种解法
    本文给出了对于《算法(第4版)》(以下简称原书或书)中的练习题3.3.20的一种解法。◆要求原书中的练习题3.3.20要求计算一棵大小为N且完美平衡的二叉查找树的内部路径长度,其中N为2的幂减1。◆解答N为2的幂减1的一颗完美平衡的二叉查找树是一棵满二叉树。在这样的......
  • 时间复杂度为 O(n^2) 的排序算法
    作者:京东保险王奕龙对于小规模数据,我们可以选用时间复杂度为O(n2)的排序算法。因为时间复杂度并不代表实际代码的执行时间,它省去了低阶、系数和常数,仅代表的增长趋势,所以在小规模数据情况下,O(n2)的排序算法可能会比O(nlogn)的排序算法执行效率高。不过随着数据规模增大,......
  • 算法与数据结构——堆排序
    堆排序堆排序(heapsort)是一种基于堆数据结构实现的高效排序算法。我们可以利用已经学过的“建堆操作”和“元素出堆操作”实现堆排序。输入数组并建立小顶堆,此时最小元素位于堆顶。不断执行出堆操作,依次记录出堆元素,即可得到从小到大排序的序列。以上方法虽然可行,但需借助一......
  • go 基于推特雪花算法生成定长id
    基于推特雪花算法生成定长id,属于int64类型。1BitUnused|41BitTimestamp|10BitNodeID|12BitSequenceID1bit未使用,默认是0。41bit存储毫秒级时间戳,当前时间与Nov04201001:42:54UTC的时间差。10bit存储节点的id,最多支持1024个。12bit自增id,同1个节点在1秒内最多......
  • 206号资源-源程序:2024年新智能优化算法-鹦鹉优化算法---------已提供下载资源
    ......
  • 数模创新算法篇 | 基于CEEMDAN分解与LSTM模型的电力负荷预测
    目录 废话不多说,直接上目录问题背景与理论1.长短期记忆网络(LSTM)理论2.CEEMDAN分解理论3.LSTM与CEEMDAN结合的优势4.应用场景与前景Python代码实操导入库和准备数据备注定义数据整理函数定义LSTM模型构建函数数据处理和模型训练评估模型性能绘制预测结果图......