垃圾回收器

535 阅读7分钟
如果说垃圾回收算法是垃圾回收的方法论,那么垃圾收集器就是内存回收的具体实现
我们在此会对各个垃圾收集器进行比较,但是垃圾收集器迄今为止还没有最好的一种,我们能做的只是根据具体场景选择出最适合的垃圾收集器


1. Serial(串行)收集器

最基本也是历史最悠久的垃圾收集器,串行收集器其实就是单线程工作,并且在进行垃圾收集时会强制性STW(停止应用程序进程),直至垃圾回收结束
新生代使用复制算法,老年代采用标记整理算法


1.1 Serial收集器的优点

简单高效
注:几乎所有单线程的实现都会在某些情况下优于其他实现,原因是单线程减少了线程上下文切换的开销

1.2 Serial Old收集器

这是Serial的老年代版本,它主要是作为CMS收集器的后备方案

2. ParNew收集器

Serial收集器的多线程版本,默认收集线程数量与Cpu核数相同.
新生代采用复制算法,老年代采用标记整理算法


3. Parallel Scavenge收集器

此收集器类似于ParNew,这是Server模式下的默认收集器,这个收集器重点关注是吞吐量,更高效的利用cpu,吞吐量就是在cpu运行时间能处理多少的任务
新生代采用复制算法,老年代采用标记整理算法

4. CMS垃圾收集器

CMS(Concurrent Mark Sweep) 收集器是一个以最短回收时间为目标的收集器,从名字我们可以看到它采用的是标记清除算法.CMS非常适合在注重用户体验的应用中使用,他是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程几乎同一时间工作

4.1 收集过程

CMS垃圾收集器的收集过程相比于前几种更复杂一些,他主要分为四个阶段
  • 初始标记:暂停其他线程,标记GC Root能够直接引用到的对象,速度很快
  • 并发标记:同时开启GC线程与用户线程,形成一个闭包结构,在这个期间会将全部的垃圾对象标记,但是这个阶段并不能标记在此阶段运行时应用线程新产生的垃圾,这个阶段是整个过程中最耗时的阶段
  • 重新标记:暂停用户线程.标记在并发标记阶段新产生的垃圾对象,这个阶段要比初始标记时间稍长,但远远短于并发标记时间
  • 并发清理:开启用户线程,清理刚刚标记的垃圾对象,这个阶段新产生的对象会等待下一次垃圾收集进行时收集

4.1.1 安全点
安全点是代码的一些特定位置,当GC发生的时候并不会马上就进行清理,而是会等所有线程达到安全点
安全点可能的位置
  • 方法返回之前
  • 方法调用之后
  • 抛出异常的位置
  • 循环的结尾

4.1.2 安全区域
安全区域是针对正在执行的线程设定的,如果一个线程处于Sleep或者中断状态时,他就不恩呢个响应jvm中断请求.
安全区域是指在一段代码内,引用关系不会发生变化

4.2 CMS的优点

CMS是并发收集,停顿时间短,非常适合注重用户体验的应用上

4.3 CMS的缺点

  • CPU资源敏感
  • 无法处理浮动垃圾
  • 采用垃圾清理算法,导致大量的内存碎片.可以通过参数设置收集后内存整理

4.4 并发失败

在并发清理阶段,如果清理过程没有进行完,此时又触发了Full GC(CMS垃圾收集器老年代触发垃圾收集的内存比例默认是92%)那么这里就称为并发失败(concurrent mode failure),并发失败回事系统进行STW,然后通过Serial Old收集器同一回收

5. G1垃圾收集器

g1垃圾收集器是面向服务器的垃圾收集器,主要针对配备有多颗处理器与大容量内存的收集器,它既能保证CPU的高吞吐,也能保证应用程序停顿时间

5.1 G1垃圾收集器的分代

G1垃圾收集器依然保留了分代的概念,如传统分代不同,G1将堆内存分为多个相同大小的Region.JVM最多有2048个Region,G1的分代与传统物理隔离的分代有明显不同,因为每一代的空间都可以是不连续的Region的集合.
注: 一个Region可能之前是年轻代,但是如果Region进行了垃圾回收之后可能就会变成老年代.也就是说Region的区域功能可能会动态变化.

5.2 G1对大对象的处理

G1有专门分配大对象的Region叫Humongous区,而不是让大对象进入老年代的Region区.比如一个Region的大小是2M,那么超过了1M的对象就会被放入到Region中,如果大对象太大可能会横跨多个连续的Region.
Humongous区专门存放短期巨型对象,不用直接进入到老年代,可以节约老年代空间,避免老年代空间不够而产生GC的开销.FullGc时会将Humongous一起回收.

5.3 G1垃圾回收步骤

G1垃圾回收的步骤与CMS很类似.前三个步骤都是相同的
  • 初始标记 : 同CMS
  • 并发标记 : 同CMS
  • 最终标记 : 同CMS重新标记
  • 筛选回收 : 这一阶段与CMS的并发清理不同,这一阶段会进行STW.并且启动多个GC线程并发回收

5.4 如何保证停顿时间

在筛选回收阶段,并不一定会回收所有所有的垃圾对象.在回收之前,首先会对Region的回收价值与回收成本做一个排序,并且根据用户指定的停顿时间来判断这个时间内可以回收多少对象,由此就可以做到保证停顿时间.

5.5 Garbage-First(优先列表)

这是G1名称的由来,G1垃圾收集器在后台维护了一个优先列表,使得垃圾收集过程中可以优先回收选择回收价值高的垃圾收集.

5.5 采用算法

G1主要采用的是复制算法,将一个Region中存活的对象直接放入到另一个Region中,因此几乎不会产生内存碎片,也就减少了内存整理的开销.

5.6 G1垃圾收集器的优势

  • 并行与并发 : G1可以充分利用多CPU,多核的硬件优势,使用多个CPU或者CPU核心来缩短STW的时间.部分其他收集器原本需要停顿java线程来执行GC,G1仍然可以通过并发的方式让java程序继续运行
  • 分代收集 : 虽然G1可以不通过其他收集器配合就能管理整个堆,但仍然保留了分代的概念
  • 空间整合 : 与CMS的标记清理不同,G1从整体看是标记整理算法,从局部看是复制算法
  • 可预测停顿 : G1除了追求低停顿外,还可以建立可预测的停顿时间模型,通过优先列表能让使用者明确指定垃圾回收的时间


5.7 G1垃圾收集分类

5.7.1 YoungGC
与普通YoungGC相同,不同的是触发时机.YoungGC触发之前会预估清理垃圾的时间是不是接近自定义清理时间,如果小于这个时间,那么G1会为Eden区分配更多的Region来等待下次回收

5.7.2 MixedGC
混合GC.G1独有,当老年代的堆占有率达到阈值时触发,回收整个堆内存,根据优先列表和预估时间来清理内存,MixedGC采用复制算法,当剩余Region空间不足则会触发FullGC

5.7.3 FullGC
STW,采用单线程收集,清理,整理压缩.以便空闲出来一部分Region来为下一次MixedGC使用,整个过程非常耗时.

6. 垃圾收集器如何选择

  • 内存小使用串行收集器
  • 单核运行,没有停顿时间要求,串行收集器
  • 允许停顿时间超出1S,并发收集器
  • 响应时间比较重要,老年代选择CMS,年轻代选择ParNew
  • 大内存多处理器服务器选择G1
  • 对停顿时间需求高选择G1