JVM垃圾回收算法

后端开发技术

来自:后端开发技术

谈到Java不得不谈GC,谈到GC不得不谈垃圾回收算法

对象已死吗

在进行垃圾回收之前,第一件事就是判断哪些对象还存活着,哪些对象已死需要被回收。

1.引用计数算法

很多判断对象是否存活的算法是这样解答的:给对象中添加一个引用计数器,每当有个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1,任何时刻计数器为0的对象就是不可能再被使用的,该对象将被回收。

客观的说,引用计数法实现简单,效率高。但是没有主流JVM选择引用计数法管理内存,因为他无法解决循环引用的问题。例如a.objB=b,b.objA=a,

此时对象a、b的计数器永远至少为1,当两个对象都不在使用时,并不会置为0,所以难以被回收。并且,引用计数器要求在每次因引用产生和消除的时候,伴随一个加法操作和减法操作,对系统性能会有一定的影响。

2.可达性分析算法

主流JVM都是称通过可达性分析来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链( Reference Chain),当一个对象到GC Roots 没有任何引用链相连,用图论的话来说,就是从GC Roots 到这个对象不可达) 时,则证明此对象是不可用的。如图所示,对象object 5、object 6、object 7 虽然互相有关联,但是它们到GC Roots 是不可达的,所以它们将会被判定为是可回收的对象。

垃圾回收算法

1.引用技术算法

前面已经介绍

2.标记-清除算法

最基础的收集算法是“标记- 清除”(Mark-Sweep) 算法,如同它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象(可达性分析)。之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其不足进行改进而得到的。

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

3.标记-整理算法(标记-压缩)

算法分为“标记”、“压缩”和“清除”三个阶段:首先标记出所有需要回收的对象,把所有存活的对象压缩到一段,然后清理掉端边界以外的内存。这样

将不会产生磁盘碎片。但是,压缩阶段占用了系统的消耗,并且如果标记对象过多的话,损耗可能会很大,在标记对象相对较少的时候,效率较高。

4.复制算法

为了解决效率问题,一种称为“复制”(Copying)  的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存话着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半,未免太高了一点。

不足:对象较多时效率低,并且有一半的空间浪费。

5.分代收集算法

目前主流JVM垃圾收集都采用“分代收集”(Generational Collection) 算法,这种算法并没有什么新的思想,只是根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保(如果有空间,通过分配担保机制进入老年代),就必须使用“标记一清理”或者“标记一整理”算法来进行回收。

对于新生代和老年代来说,通常新生代回收的频率很高,但是每次回收的时间都很短,而老年代回收的频率比较低,但是被消耗很多的时间。为了支持高频率的新生代回收,虚拟机可能使用一种叫做卡表的数据结构,卡表为一个比特位集合,每一个比特位可以用来表示老年代的某一区域中的所有对象是否持有新生代对象的引用,

这样以来,新生代GC时,可以不用花大量时间扫描所有老年代对象,来确定每一个对象的引用关系,而可以先扫描卡表,只有当卡表的标记为1时,才需要扫描给定区域的老年代对象,而卡表为0的所在区域的老年代对象,一定不含有新生代对象的引用。

卡表中每一位表示老年代4KB的空间,卡表记录为0的老年代区域没有任何对象指向新生代,只有卡表为1的区域才有对象包含新生代对象的引用,因此在新生代GC时,只需要扫面卡表为1所在的老年代空间,使用这种方式,可以大大加快新生代的回收速度。

6.分区算法

分代算法将按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成不同小区间。每个小区间都独立使用,独立回收。这种算法的好处是可以控制一次回收多少个小区间。

一般来说,在相同的条件下,堆空间越大,一次GC时所需要的时间就越长,从而产生的停顿也越长。为了更好地控制GC产生的停顿时间,将一块大的内存区域分割为多个小块,根据目标的停顿时间,每次合理的回收若干个小区间,而不是整个堆空间,从而减少一次GC所产生的停顿。

(0)

相关推荐

  • 7种jvm垃圾回收器,这次全部搞懂

    前言 之前我们讲解了jvm的组成结构与垃圾回收算法等知识点,今天我们来讲讲jvm最重要的堆内存是如何使用垃圾回收器进行垃圾回收,并且如何使用命令去配置使用这些垃圾回收器. 堆内存详解 上面这个图大家应 ...

  • 搞定这24道JVM面试题,要价30k都有底气~

    回复"面试"获取全套面试资料 1.什么是JVM? JVM 的全称是 「Java Virtual Machine」,也就是我们耳熟能详的 Java 虚拟机.它能识别 .class后缀 ...

  • Java虚拟机垃圾回收(三) 7种垃圾收集器

    主要特点 应用场景 设置参数 基本运行原理 在<Java虚拟机垃圾回收(一) 基础>中了解到如何判断对象是存活还是已经死亡?在<Java虚拟机垃圾回收(二) 垃圾回收算法>了解 ...

  • 2021最新 Java虚拟机(JVM)面试题精选(附刷题小程序)

    推荐使用小程序阅读 为了能让您更加方便的阅读 本文所有的面试题目均已整理至小程序<面试手册> 可以通过微信扫描(或长按)下图的二维码享受更好的阅读体验! 目录 推荐使用小程序阅读 1. J ...

  • JVM真香系列:如何判断对象是否可被回收?

    回复"000"获取大量电子书 在JVM中程序寄存器.Java虚拟机栈.本地方法栈,这三个区是随着线程的创建而创建,随着线程结束而销毁. 其实就是这三个的生命周期和线程的生命周期一样 ...

  • 程序员登高之路——JAVA篇——2.JVM的垃圾回收

    如何判断对象死亡? 目前主流的判断对象死亡的方法有两种: 1.引用计数法: 每个对象对象包含一个引用计数器,每当对象被引用,引用计数器便加一,引用失效就减一.当对象的引用计数器为0时,则表示对象可被回 ...

  • JVM之垃圾回收过程

    本文主要学习自<深入理解Java虚拟机>,内容顺序大致遵循书本顺序,加上自身对于其中算法的理解学习.今天小泉主要跟大家一起学习一下垃圾回收的整个流程. 前言 垃圾回收(Garbage Co ...

  • 探索G1垃圾回收器

    前言 上篇JVM的文章中我们对ParNew和CMS垃圾回收器已经有了一个比较透彻的认识,感兴趣的小伙伴可以去回看一下探索ParNew和CMS垃圾回收器. 今天我们继续探索垃圾回收器G1的原理,让我们开 ...

  • Java虚拟机(JVM)面试题(2020最新版)

    大家好,我是CSDN的博主ThinkWon,"2020博客之星年度总评选'开始啦,希望大家帮我投票,每天都可以投多票哦,点击下方链接,然后点击'最大",再点击'投TA一票'就可以啦 ...

  • java开发之JVM基础知识分享

    虚拟机运行机制 JVM运行在操作系统上,不与硬件设备直接交互. Java程序执行流程:Java源代码文件( Hello.java)被编译器编译成字节码文件( Hello.class),字节码文件被JV ...

  • 应该是全网最全的JVM知识点总结

    应该是全网最全的JVM知识点总结