JVM——垃圾收集器

JVM垃圾收集器

HotSpot虚拟机中实现了如下7种垃圾收集器,“人无完人,金无足赤”,连垃圾处理器也是这样,每种收集器都有各自的优缺点,我们选择的只是针对具体场景最合适的收集器。两个收集器之间的连线表示它们可以搭配来使用。

在这里插入图片描述

首先明确两个概念:并发并行

并行:指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。

并发:指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。

Minor GC,Major GC 和 Full GC

这里先补充前面遗漏的一个知识点

Minor GC

Minor GC是对新生代的GC,当新生代无法为新生对象分配内存空间的时候,就会触发Minor GC。因为新生代中大多数对象的生命周期都很短,所以发生Minor GC的频率很高,虽然它会stop the world(暂停所有GC以外的线程),但是它的回收速度很快。

Major GC

Major GC是针对老年代的GC。通常Major GC 是由 Minor GC 触发的。

Full GC

Full GC是对整个新生代、老生代、元空间(java8以上版本取代perm gen)的全局范围的GC。Full GC不等于Major GC,也不等于Minor GC+Major GC,发生Full GC需要看使用了什么垃圾收集器组合,才能解释是什么样的垃圾回收。

安全点和安全区域

OopMap

垃圾收集时,GC线程会对栈上的内存进行扫描,看看哪些位置存储了 Reference 类型。如果发现某个位置确实存的是 Reference 类型,就意味着它所引用的对象这一次不能被回收,而那些非 Reference 类型的数据在GC时毫无用处,但我们还是不得不对整个栈全部扫描一遍,这是对时间和资源的一种浪费。

OopMap的作用:OopMap 记录了栈上本地变量到堆上对象的引用关系。它就是为了解决上面这个问题,将栈中的Reference类型的位置全部记录下来,这样在GC时就能直接读取它们的位置,避免全栈扫描,节省时间提高效率。

安全点

因为可达性分析算法必须是在一个确保一致性的内存快照中进行。如果在分析的过程中对象引用关系还在不断变化,分析结果的准确性就不能保证。安全点就是在某些特定的位置,所有工作线程的状态是确定的,JVM 就可以安全地执行 GC 。

  • 如何选定安全点?

    安全点太多,GC 过于频繁,增大运行时负荷;安全点太少,GC 等待时间太长。

    一般会在如下几个位置选择安全点:

    1、循环的末尾

    2、方法临返回前

    3、调用方法之后

    4、抛异常的位置

安全区域

Safe Point 是对正在执行的线程设定的。而如果一个线程处于 Sleep 或中断状态,它就不能响应 JVM 的中断请求,再运行到 Safe Point 上。因此 JVM 引入了安全区域。

安全区域是指在一段代码片段中,引用关系不会发生变化。在这个区域内的任意地方开始 GC 都是安全的。

线程在进入 Safe Region 的时候先标记自己已进入了 Safe Region,等到被唤醒时准备离开 Safe Region 时,先检查能否离开,如果 GC 完成了,那么线程可以离开,否则它必须等待直到收到安全离开的信号为止。

Serial / Serial Old收集器

这是最基本,发展历史最悠久的收集器。前者用于收集新生代,后者收集老年代

  • 特点:单线程,并发收集
  • 优点:简单高效,没有线程交互的开销
  • GC算法:Serial:复制算法 / Serial Old:标记-整理算法
  • GC区域:Serial:新生代 / Serial Old:老年代
  • 工作流程:可以看到,当这两种收集器工作时所有用户线程都必须暂停工作。

在这里插入图片描述

ParNew收集器

这个收集器其实就是Serial收集器的多线程版本。

  • 特点:多线程并发收集,收集时其他工作线程必须暂停(“stop the world”)
  • GC算法:复制算法
  • GC区域:新生代
  • 与其他收集器配合:目前只有ParNew收集器能与CMS收集器配合工作
  • 工作流程:如下图

ParNew

Parallel Scavenge / Parallel Old收集器

又称**“吞吐量优先”收集器**,和ParNew收集器类似,也是并行的多线程收集器。但是它关注点是尽可能缩短GC时用户线程的停顿时间,以尽量达到一个可控制的吞吐量(吞吐量:CPU用于运行用户代码的时间 / CPU总消耗时间,即运行用户代码时间 / 垃圾收集时间)。

  • 特点:自适应性(虚拟机会根据当前系统运行情况动态调整几个参数来提供合适的停顿时间或最大的吞吐量),多线程并发收集
  • GC算法:复制算法 / 标记-整理算法
  • GC区域:新生代 / 老年代
  • 工作流程:和上面ParNew的流程图很类似

CMS收集器

CMS即Concurrent Mark Sweep,这是一种以获取最短回收停顿时间为目标的收集器

  • 优点:并发收集,低停顿

  • GC算法:标记-清除算法

  • 缺点

    • 对CPU资源非常敏感,并发时会占用部分线程而导致应用变慢,吞吐量降低
    • 无法处理浮动垃圾(在CMS并发清理时,用户线程运行时产生的垃圾只能在下一次GC中清理)
    • 可能会产生大量内存空间碎片(这是标记-清除算法带来的缺点)
  • 收集过程

    图中蓝色箭头表示GC线程,白色箭头表示用户线程。

    其中初始标记重新标记期间所有用户线程暂停;除重新标记是多线程(多个GC线程同时工作),其他步骤都是单线程(只有一个GC线程工作)。

    初始标记重新标记两步需要 ”stop the world“

    1. 初始标记:标记GC Roots能直接关联到的对象。速度很快。
    2. 并发标记:通过从GC Roots向下用可达性分析算法找出存活的对象。速度慢。
    3. 重新标记:修正并发标记时因用户程序继续运行导致标记产生变动的那部分对象的标记记录。速度在初始标记和并发标记之间。
    4. 并发清除:清除上面被标记为垃圾的对象。速度慢(因为是标记-清除算法)

工作流程图如下:

CMS

G1收集器

G1(Garbage-First)收集器是一款面向服务器的垃圾收集器。使用G1收集器时,它将整个Java堆划分为多个大小相等的独立区域(Region),它跟踪各个Region中垃圾堆积的价值大小,在后台维护一个优先列表,每次根据允许的收集时间优先回收价值最大的Region。

  • 特点

    • 并行与并发:G1能充分利用多CPU的优势缩短“stop the world”的时间
    • 分代收集:G1它采用不同的方式处理堆中的新生代和老年代
    • 空间整合:整体上基于标记-整理算法;局部上基于复制算法,采用两种算法都不会产生内存碎片
    • 可预测的停顿:可以建立可预测的停顿时间模型:即让 使用者 明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得从超出N毫秒
  • 工作流程

    下面几个步骤和CMS的步骤类似。

    初始标记并发标记两步是单个GC线程,而最终标记筛选回收两步是多GC线程。

    四个步骤中只有并发标记可以和用户线程并行执行,其他三步都需要 “stop the world”

    1. 初始标记:与CMS第一步相同,标记GC Roots能直接关联到的对象。

      速度很快。

    2. 并发标记:与CMS第二步相同,通过从GC Roots向下用可达性分析算法找出存活的对象。速度慢。

    3. 最终标记:修正并发标记时因用户程序继续运行导致标记产生变动的那部分对象的标记记录,将这些对象记录的数据更新到Remember Set中。(G1中每个Region都会维护一个Remember Set用来记录不同Region中对象的引用情况。Remember Set的作用就类似于OopMap,用来避免全堆扫描。)

      速度介于前两部之间。

    4. 筛选回收:首先对各个Region的回收价值和成本进行排序,根据用户期望的GC停顿时间来制定回收计划,只回收一部分Region

      速度慢。

G1

参考资料

《深入理解Java虚拟机》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值