jvm垃圾回收算法详细解析

目录
  • 前言
  • 几种常用的垃圾回收算法
    • 1、引用计数法
    • 2、根搜索算法
    • 3、标记清除法(Mark-Sweep)
    • 4、复制交换算法(Mark-Sweep)
    • 5、标记压缩算法(Mark-Compact)
  • JVM 分代收集算法

前言

相比C语言,JVM虚拟机一个优势体现在对对象的垃圾回收上,JVM有一套完整的垃圾回收算法,可以对程序运行时产生的垃圾对象进行及时的回收,以便释放JVM相应区域的内存空间,确保程序稳定高效的运行,但在真正了解垃圾回收算法之前,有必要对JVM的对象的引用做一个简单的铺垫

JVM对象可达性分析算法 Java虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象扫描堆中的对象,看是否能够沿着GC Root对象为起点的引用链找到该对象,找不到表示可以被回收

想象一下,对象在什么情况下会被认为是垃圾对象呢?

当一个对象没有被任何引用了,就认为对象无用了(如图一)当一组对象没有被任何引用了,可以认为这组对象无用了(如图二)

下面通过一段简单的程序代码,在不同的时刻导出dump日志,利用MAT分析工具来说明下这个问题

public static void main(String[] args) throws Exception{
        List<Object> list1 = new ArrayList<>();
        list1.add("aaa");
        list1.add("bbb");
        System.out.println(1);
        System.in.read();
        list1 = null;
        System.out.println(1);
        System.in.read();
        System.out.println("end");
    }

运行程序,利用下面的命令导出两个时刻的dump文件

map -dump:format=b,live,file=a.dump 17356

分析第一个文件,在第一个时刻,此时由于list1集合中放了2个元素,因此该对象存在引用关系

再分析第二个文件,在第二个时刻,此时由于list1置为null,因此该对象没有被引用的地方了,在gc root中找不到list1对象了

通过上面简单的案例演示和说明,我们再次明确,对象被标为垃圾的前提是该对象从GC Root出发进行搜索时,找不到对该对象的引用,即为不可达对象

几种常用的垃圾回收算法

1、引用计数法

引用计数法在JVM的早期版本中有用到,引用计数是指采用计数器说明引用对象的个数,即为某个对象设置一个引用对象数量的计数器,如果该对象被引用了,计数器的数量加1,否则减一,当计数器的数值为0的时候,垃圾回收器将该对象进行回收

如下图所示,某一时刻,对象A,B,C各自持有对对象P的引用,到另一时刻A,B,C不再对P对象进行引用了,计数器的值归为0,此时垃圾回收器就对P对象进行垃圾回收

引用计数法在JVM垃圾回收算法中逐渐被废弃,很简单,如果存在对象之间的循环引用,则计数器的count值永远不会清0,如此对象将会一直存在内存中得不到释放

2、根搜索算法

根搜索算法是JVM的默认垃圾回收算法,也叫做“可达性分析算法”,即从GCRoot出发,有引用的对象都是不可回收的,其他的可以进行标记后再回收

如下图所示,对某个线程栈来说,里面有局部变量,有静态变量,常量池,或对本地native方法的调用,假设从某个栈帧的局部变量出发,可认为是GCRoot的搜索起点,以此为起点,搜索整个引用链条上的所有引用对象,在这个链条上的对象认为是GCRoot可达的对象,否则将会被设为可回收对象被垃圾回收器回收

3、标记清除法(Mark-Sweep)

标记清除算法主要经历标记和清除2个步骤,通过根搜索算法中的可达性分析之后,那些被标记为垃圾对象的内存空间,通过该算法直接清除

标记清除算法简单粗暴,效率很高,因为不涉及到其他的步骤,但是从下面的图示也可以看到,标记清除算法产生了不连续的内存碎片(产生了内存间隙)导致其内存使用率比较低,如果程序中需要为某个大对象分配一大块连续的内存空间,则很难通过这种算法腾出这样的内存空间,因此该算法在JVM中并没有使用到(作为一种垃圾回收算法的思想值得借鉴)

4、复制交换算法(Mark-Sweep)

为解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当某一块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题

在堆的年轻代进行GC的时候使用的就是复制算法,还记得新生代的区域划分吗?S0和S1两个区域的内存在实际使用时,总是其中一块在使用,当达到了minor gc的条件时,就按照复制算法,将这块空间中存活的对象转移到另一块,再清理当前这块空间的垃圾对象

总结来说,复制算法速度快,无内存碎片,但缺点也比较明显,就是浪费空间,在年轻代的 Minor Gc中使用

5、标记压缩算法(Mark-Compact)

为解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存

标记压缩算法整个垃圾回收的过程包括,标记 -> 压缩 -> 整理 ->清除,通过下图展示也不难发现,标记压缩进行垃圾回收之后,整个内存区域的分布比较连续(无内存碎片),但是很明显这种算法的中间操作步骤相对上面两种算法要复杂,因此在进行GC过程中比较耗时效率较低,在老年代的Full Gc时使用的就是标记压缩算法

JVM 分代收集算法

在JVM的内存结构中,按照堆内存的结构划分,大的方面可以分为年轻代和老年代,堆内存是JVM中进行垃圾回收的主要区域

但是各个区域在使用过程中的作用,对象生成规则,对象生命周期的不同又可以细分为各个逻辑上的结构,比如在新生代区域,可以划分为 Eden区和Survivor区,而Survivor再细分为from(s0)区和to(s1)区,通过区域的划分,各个区域的职责更加明确

我们知道,新生代和老年代一个很大的区别在于,新生代是对象频繁产生的区域,也是Minor Gc很频繁的区域,而老年代中的对象大多则是比较稳定的对象,从这个角度上说,各个区域在进行垃圾回收时策略自然不相同

分代收集算法是目前大部分JVM的垃圾收集器采用的算法,新生代对象朝生夕死,生命周期短,内存空间需要频繁的进行清理以应对快速而来的新对象,因此需要更高效的垃圾回收算法

新生代

目前大部分垃圾收集器对新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间(一般为8:1:1),每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间

老年代

老年代内存空间相对较大,可以存放更多的对象,通常情况下每次发生Full Gc的间隔时间也较长,而且在老年代中经常需要存放一些大对象,需要连续的内存空间,基于这些特点,在目前主流的JVM垃圾回收器中对于老年代采用压缩算法

到此这篇关于jvm垃圾回收算法详细解析的文章就介绍到这了,更多相关jvm垃圾回收内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入理解JVM垃圾回收算法

    目录 一.垃圾标记阶段 1.1.引用计数法 (java没有采用) 1.2.可达性分析算法 二.对象的finalization机制 2.1.对象是否"死亡" 三.使用(MAT与JProfiler)工具分析GCRoots 3.1.获取dump文件 3.2.GC Roots分析 四.垃圾清除阶段 4.1.标记-清除算法 4.2.复制算法 4.3.标记-压缩(整理,Mark-Compact)算法 4.4.以上三种垃圾回收算法对比 4.5.分代收集算法 4.6.增量收集算法 4.7.分区算法G1

  • JVM垃圾回收算法的概念与分析

    前言 在JVM内存模型中会将堆内存划分新生代.老年代两个区域,两块区域的主要区别在于新生代存放存活时间较短的对象,老年代存放存活时间较久的对象,除了存活时间不同外,还有垃圾回收策略的不同,在JVM中中有以下回收算法: 标记清除 标记整理 复制算法 分代收集算法 有了垃圾回收算法,那JVM是如果确定对象是垃圾对象的呢?判断对象是否存活JVM也会有几套自己判断算法了: 引用记数 可达性分析 有了垃圾回收和判断对象存在这两个概念后,再来逐步分析它们. JVM是如何判断对象是否存活的? 要是让开发人员来

  • 浅谈JVM垃圾回收有哪些常用算法

    一.前言: 垃圾回收: 在未来的JDK中可能G1会为ZGC所取代 先问自己几个问题: 什么是垃圾? 垃圾就是堆内存中(范指)没有任何指针指向的对象实体.不具有可达性. 为什么要回收垃圾? 因为我们的内存是有限的,内存长时间不清理就会导致内存溢出,OOM: 只要是程序正在跑,那么就不断生成新的对象,我们需要GC开辟新的空间分配给新的对象. 我们怎么回收垃圾? 依靠Java的自动内存回收机制,机制的优劣由算法决定: 或者说是机制的适配度由算法和应用场景共同决定. 什么时候回收垃圾? 当堆中的实体对象

  • JVM的垃圾回收算法一起来看看

    目录 垃圾回收算法 概念 1.标记算法 1.1引用计数法(ReferenceCounting) 1.2可达性分析算法(ReachableAnalysis) 2.回收算法 2.1标记清除算法(MarkSweep) 2.2复制算法(Copying) 2.3标记压缩算法(Mark-Compact) 2.4分代回收算法 总结 垃圾回收算法 概念 垃圾回收(Garbage Collection,GC).程序的运行需要资源,无效的对象如果不及时清理就会一直占用资源,所以对内存资源管理就变得十分重要.而Jav

  • 最新JVM垃圾回收算法详解

    目录 1.垃圾回收需要做什么 2.如何判断对象可被回收 2.1 引用计数算法 2.1.2 优点 2.1.2 缺点 2.2 可达性分析算法 2.2.1 算法思路 2.2.2 GC Roots对象(两栈两方法) 2.2.3 优点 2.2.4 缺点 3.判断对象生存还是死亡 3.1 两次标记过程 3.2 finalize()方法 4.HotSpot虚拟机中对象可达性分析的实现 4.1 枚举根节点 4.2 安全点 4.2.1 安全点是什么,为什么需要安全点 4.2.2 安全点的选定 4.2.3 如何在安

  • JVM的垃圾回收算法工作原理详解

    怎么判断对象是否可以被回收? 共有2种方法,引用计数法和可达性分析 1.引用计数法 所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一.当一个对象的引用计数器为零时,说明此对象没有被引用,也就是"死对象",将会被垃圾回收. 引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象A引用对象B,对象B又引用者对象A,那么此时A,B对象的引用计数器都不为零,也就造成无法完成垃圾回收,所以主流的虚拟机都没有采用这种算法

  • 图解JVM垃圾内存回收算法

    前言 首先,我们要讲的是JVM的垃圾回收机制,我默认准备阅读本篇的人都知道以下两点: JVM是做什么的 Java堆是什么 因为我们即将要讲的就是发生在JVM的Java堆上的垃圾回收,为了突出核心,其他的一些与本篇不太相关的东西我就一笔略过了 众所周知,Java堆上保存着对象的实例,而Java堆的大小是有限的,所以我们只能把一些已经用完的,无法再使用的垃圾对象从内存中释放掉,就像JVM帮助我们手动在代码中添加一条类似于C++的free语句的行为 然而这些垃圾对象是怎么回收的,现在不知道没关系,我们

  • jvm垃圾回收算法详细解析

    目录 前言 几种常用的垃圾回收算法 1.引用计数法 2.根搜索算法 3.标记清除法(Mark-Sweep) 4.复制交换算法(Mark-Sweep) 5.标记压缩算法(Mark-Compact) JVM 分代收集算法 前言 相比C语言,JVM虚拟机一个优势体现在对对象的垃圾回收上,JVM有一套完整的垃圾回收算法,可以对程序运行时产生的垃圾对象进行及时的回收,以便释放JVM相应区域的内存空间,确保程序稳定高效的运行,但在真正了解垃圾回收算法之前,有必要对JVM的对象的引用做一个简单的铺垫 JVM对

  • Python内存管理方式和垃圾回收算法解析

    概要 在列表,元组,实例,类,字典和函数中存在循环引用问题.有 __del__ 方法的实例会以健全的方式被处理.给新类型添加GC支持是很容易的.支持GC的Python与常规的Python是二进制兼容的. 分代式回收能运行工作(目前是三个分代).由 pybench 实测的结果是大约有百分之四的开销.实际上所有的扩展模块都应该依然如故地正常工作(我不得不修改了标准发行版中的 new 和 cPickle 模块).一个叫做 gc 的新模块马上就可以用来调试回收器和设置调试选项. 回收器应该是跨平台可移植

  • JVM垃圾回收原理解析

    概述 Java运行时区域中,程序计数器,虚拟机栈,本地方法栈三个区域随着线程的而生,随线程而死,这几个区域的内存分配和回收都具备确定性,不需要过多考虑回收问题.而Java堆和方法区则不一样,一个接口的多个实现类需要的内存不一样,一个方法的多个分支需要的内存可能也不一眼,我们只有在运行期,才能知道会创建的对象,这部分的内存分配和回收,是垃圾回收器所关注的.垃圾回收器需要完成三个问题:那些内存需要回收:什么时候回收以及如何回收. 那些垃圾需要回收 垃圾回收的基本思想是考察一个对象的可达性,即从根节点

  • jvm垃圾回收GC调优基础原理分析

    目录 核心概念(Core Concepts) Latency(延迟) Throughput(吞吐量) Capacity(系统容量) 相关示例 Tuning for Latency(调优延迟指标) Tuning for Throughput(吞吐量调优) Tuning for Capacity(调优系统容量) 说明: Capacity: 性能,能力,系统容量; 文中翻译为”系统容量“; 意为硬件配置. GC调优(Tuning Garbage Collection)和其他性能调优是同样的原理.初学者

随机推荐