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