0
点赞
收藏
分享

微信扫一扫

JVM学习笔记之垃圾回收器(上)

慕犹清 2022-04-30 阅读 61

JVM学习笔记之垃圾回收器(来自尚硅谷宋红康jvm教学视频)


文章目录


前言

垃圾收集器(Garbage Collection,GC)
在java虚拟机规范中,对于垃圾收集器应该如何实现并没有进行过多的规定,因此不同的厂商、不同版本的jvm所包含的垃圾收集器可能有很大的差别。以下垃圾收集器的讨论基于HotSpot虚拟机。


一、垃圾收集器分类

1.按线程数分

可以分为串行垃圾收集器和并行垃圾收集器(此处的线程之针对与用于垃圾收集的线程,不涉及用户线程。)
来自与尚硅谷宋红康jvm

  • 串行收集是指在同一时间段内只允许有一个cpu用于执行垃圾收集操作,此时用户线程被中断(Stop The World,STW),直到垃圾收集线程结束。
    在单cpu或者较小的应用内存等硬件平台不是特别优越的场合,串行收集器的性能表现可以超过并行收集器和并发收集器,因为串行收集器不用考虑线程切换的资源和时间开销。所以,串行收集一般被应用在客户端的Client模式下的JVM中。
    在并发能力较强的CPU中,并行收集器产生的停顿时间要短于串行收集器。

  • 并行收集是指运用多个cpu同时刻执行垃圾收集,因此提升了应用的吞吐量。

  • 不管是串行还是并行的垃圾收集器,都采用独占式,使用STW机制

2.按工作模式分

可分为并发式垃圾收集器和独占式垃圾收集器
来自于尚硅谷宋红康jvm

  • 并发式垃圾收集器是指,垃圾收集线程与用户线程并发执行,以尽可能减少用户线程的停顿时间
  • 独占式垃圾收集器是指,垃圾收集线程一旦运行,就中断所有用户线程,直到垃圾收集过程完成。例如第一点所说的串行垃圾收集器和并行垃圾收集器

3.按碎片处理方式分,

可分为压缩式垃圾收集器和非压缩式垃圾收集器

  • 压缩式垃圾收集器可以理解为,在垃圾收集完成后,还对存活对象和堆空间碎片进行整理。非压缩式收集器则不进行这些操作

二、评估GC的性能指标

  • 吞吐量:运行用户代码的时间栈总运行时间的比例(总运行时间= 程序运行时间+垃圾收集时间)
  • 垃圾收集开销:吞吐量的补数,即垃圾收集时间占总运行时间的比例

三、7款经典的垃圾收集器

  • 串行垃圾收集器:Serial、Serial Old
  • 并行垃圾收集器:ParNew、Parallel Scavenge、Parallel Old
  • 并发垃圾收集器:CMS(Concurrent-Mark-Sweep)、G1(Garbage First)

来自《深入理解Java虚拟机》
上图展示了7款分代收集器的搭配关系。如果两个收集器之间存在连线,就说明他们可以搭配使用。上半部分的收集器用于新生代,下半部分的收集器用于老年代。G1收集器不同于其他6种,可以用于整个堆空间。在JDK9及以后,Serial和CMS 不能搭配使用,ParNew 和Serial Old不能搭配使用。同时在JDK9中,CMS收集器也被声明为废弃(但可以使用),直到JDK14才被删除。除此之外在JDK14中,弃用了Parallel Scavenge 和Serial Old 的组合。

1.Serial收集器:串行回收

Serial收集器是最基本、历史最悠久的垃圾收集器,在JDK1.3.1之前是HotSpot虚拟机新生代收集器的唯一选择。该收集器采用复制算法(垃圾收集算法之一)、串行收集和STW机制的方式执行垃圾收集和内存回收。
处理新生代之外,Serial收集器还提供用于执行老年代的垃圾收集器——Serial Old 收集器。Serial Old 收集器同样采用了串行收集和STW机制,但是使用的是标记-压缩算法。
- Serial Old 是运行在Client模式下默认的老年代的垃圾收集器
- Serial Old 在Server模式下主要有两个用途:① 与新生代的Parallel Scave 收集器搭配使用;② 所谓老年代CMS收集器的后备垃圾收集方案(CMS垃圾收集失败后启用)

来自《深入理解Java虚拟机》
优势:

  • 迄今为止,它依然是HotSpot虚拟机运行在客户端模式下的默认新生代收集器,有着优于其他收集器的地方——简单而高效(与其他收集器的单线程比较),对于内存资源受限的环境,它是所有收集器里额外内存消耗最小的;对于限定单个CPU的环境来说,Serial 收集器由于没有线程切换和交互的开销,可以获得最高的单线程收集效率。

    在用户的桌面应用场景中,可用内存一般不大(几十MB至一两百MB),可以在较短时间内完成垃圾收集,只要不频繁发生GC,使用串行收集器是可以接受的。所以,Serial收集器对于运行在客户端模式下的虚拟机来说是一个很好的选择。

2.PatNew收集器:并行回收

ParNew收集器实质上是Serial收集器的多线程版本,除了同时使用多条线程进行垃圾收集之外,其余的行为包括Serial收集器可用的所有参数、收集算法、STW、对象分配原则、回收策略等都与Serial收集器完全一致。
ParNew收集器是很多JVM运行在Server模式下新生代的默认垃圾收集器。通常搭配Serial Old 垃圾收集器和CMS垃圾收集器使用(JDK9以前)。
JDK9以后,ParNew加CMS的组合不再是官方推荐的服务端模式下的收集器解决方案。在这里插入图片描述

  • 对于新生代,回收次数频繁,使用垃圾收集线程并行的方式更高效

虽然ParNew收集器是基于并行回收,但是不能断定ParNew收集器的回收效率在任何场景下都会比Serial收集器更加高效。ParNew收集器运行在多cpu的环境下,由于可以充分利用多cpu、多核心等硬件资源,可以更快完成垃圾回收,提升程序的吞吐量。但在单cpu的环境下,ParNew收集器不会比Serial收集器更高效。虽然Serial收集器是基于串行回收。但是cpu不需要频繁进行线程切换,因此可以有效避免多线程交互过程中产生的一些额外开销。

3.Parallel Scavenge收集器:吞吐量优先

Parallel Scaven收集器同样是基于标记-复制算法实现的收集器,也是能够并行收集的多线程收集器。它和ParNew有以下两点不同:

  • Parallel Scavenge收集器的目标是达到一个可控制的吞吐量,它也被称为吞吐量优先的垃圾收集器。
  • 自适应调节策略也是Parallel Scavenge与ParNew的一个重要区别。

高吞吐量则可以高效率地利用cpu时间、尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互地任务。因此该收集器常在服务器环境中使用。例如执行批量处理、订单处理、工资支付、科学计的应用程序
Parallel收集器在JDK1.6时提供了用于执行老年代垃圾收集的Parallel Old收集器。Paralle Old收集器采用了标记-压缩算法,但同样也是基于并行回收和STW机制。在吞吐量优先的应用场景中,Parallel收集器和Parallel Old收集器的组合在Server模式下的内存回收性能很不错。在JDK8中,默认时这两个组合的垃圾收集器。
在这里插入图片描述

4.CMS收集器:低延迟

在JDK1.5,HotSpot推出了一款在强交互应用中几乎可认为有划时代意义的垃圾收集器:CMS(Concurrent Mark Sweep)收集器,这款收集器是HotSpot虚拟机中第一款真正义以上的并发收集器,他第一次实现了让垃圾收集与用户线程同时工作。
CMS收集器的目标是获得最短回收时间,目前很大一部分Java应用集中在互联网网站或者基于浏览器的B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS收集器就非常符合这类应用的需求。
CMS收集器的垃圾收集算法采用标记-清楚算法,并且也会STW。不幸的是,CMS作为老年代的收集器,却无法与Parallel Scaven收集器配合工作,
只能与ParNew和Serial收集器配合使用。在G1收集器出现之前,CMS使用还是非常广泛,一直到今天,仍然有很多系统使用CMS
在这里插入图片描述
它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为四个步骤,包括:
1)初始标记(CMS initial mark)
2)并发标记(CMS concurrent mark)
3)重新标记(CMS remark)
4)并发清除(CMS concurrent sweep)

  • 初始标记(Initial-Mark)阶段:在这个阶段中,程序中所有的工作线程都将会因为“Stop-the-world”机制而出现短暂的暂停,这个阶段的主要任务仅仅只是标记出GC Roots能直接关联到的对象。一旦标记完成之后就会恢复之前被暂停的所有应用线程。由于直接关联对象比较小,所以这里的速度非常快。
  • 并发标记(Concurrent-Mark)阶段:从GC Roots的直接关联对象开始遍历整个对
    象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。
  • 重新标记(Remark)阶段:由于在并发标记阶段中,程序的工作线程会和垃圾收集
    线程同时运行或者交叉运行,因此为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短。
  • 并发清除(Concurrent-Sweep)阶段:此阶段清理删除掉标记阶段判断的已经死亡的对象,释放内存空间。由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。

尽管CNS收集器采用的是并发回收(非独占式),但是在其初始化标记和再次标记这两个阶段中仍然需要执行“Stop-the-world”机制暂停程序中的工作线程,不过暂停时间并不会太长,因此可以说明目前所有的垃圾收集器都做不到完全不需要“Stop-the-wor1d”,只是尽可能地缩短暂停时间。
由于最耗费时间的并发标记与并发清除阶段都不需要暂停工作,所以整体的回收是低停顿的。
另外,由于在垃圾收集阶段用户线程没有中断,所以在CMS回收过程中,还应该确保应用程序用户线程有足够的内存可用。因此,CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,而是当堆内存使用率达到某一阙值时,便开始进行回收,以确保应用程序在CMS工作过程中依然有足够的空间支持应用程序运行。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这时虚拟机将启动后备预案:临时启用serial old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。
CMS收集器的垃圾收集算法采用的是标记-清除算法,这意味着每次执行完内存回收后,由于被执行内存回收的无用对象所占用的内存空间极有可能是不连续的一些内存块,不可避免地将会产生一些内存碎片。那么CMS在为新对象分配内存空间时,将无法使用指针碰撞(Bump the Pointer)技术,而只能够选择空闲列表(Free List)执行内存分配。

举报

相关推荐

0 条评论