这里介绍一下各个垃圾收集器的作用,在理解各个垃圾收集器之前,你应该熟悉垃圾收集器的算法和分类
新生代串行收集器
特点
- 仅仅使用单线程进行垃圾回收
- 独占式的垃圾回收
- 使用的复制算法
优点
- 是一个成熟、经过长时间生成环境考验的极为高效的收集器
- 使用复制算法,实现相对简单,逻辑处理特别高效,且没有线程切换的开销
缺点
- 运行时,JAVA应用程序中的线程都需要暂停进行等待。出现“Stop the World”现象
- 用户体验非常糟糕,在实时性要求较高的应用场景,这种现象往往是不能接受的
老年代串行收集器
特点
- 仅仅使用单线程进行垃圾回收
- 独占式的垃圾回收
- 使用标记 - 压缩算法
优点
- 老年代串行收集器可以和多种新生代回收器配合使用,同时他也可以作为CMS回收器的备用回收器。
缺点
- 如果堆空间比较大,一旦老年串行收集器启动,应用程序很可能会因此停顿几秒甚至更长时间
并行收集器
特点
- 工作在新生代的垃圾收集器
- 简单的将串行回收器多线程化
- 回收策略、算法、以及参数和串行回收器一样
- 独占式的回收器
- 并行的线程数,当CPU小于等于8个时,线程数尽量和CPU个数一直,当CPU个数大于8个时,线程数等于 3 +((5*cpu_count)/8)
优点
- 在并发能力较强的CPU上,停顿时间明显比串行收集器短
缺点
- 也需要暂定程序运行
- 如果CPU并发能力比较弱,并行回收器的效果不会比串行回收器好
新生代并行回收(Parallel Scavenge)收集器
特点
- 它非常关注系统的吞吐量
- 和并行收集器一样,都是多线程、独占式收集器
- 可以通过
-XX:MAXGCPauseMillis
设置最大垃圾收集停顿时间,如果时间太小,可能会导致回收频繁,从而降低了吞吐量 - 可以通过
-XX:GCTimeRatio
设置吞吐量大小,参数为n,那么垃圾回收时间不超过 1 1 + n \frac{1}{1+n} 1+n1 ,比如:n = 99,那么垃圾回收时间不超过1% - 可以通过
-XX:+UseAdaptiveSizePolicy
设置为自适应GC策略,系统可以自动配置最大堆、吞吐量和停顿时间的平衡
老年代并行回收收集器
- 关注吞吐量的收集器
- 多线程并发收集器
- 采用标记 - 压缩算法
- 通过
-XX:ParallelGCThreads
设置垃圾回收时的线程数量
CMS收集器
- Concurrent Mark Sweep:并发标记清除,使用的是标记 - 清除算法
- 主要关注系统的停顿时间,他是并行回收非独占的垃圾收集器
- 初始标记、重新标记会独占系统资源,其他时候不会,所以整体上,他不是独占式的
步骤
- 初始标记、并发标记、重新标记、并发清除、并发重置
- 初始标记、并发标记、重新标记:都是为了标记处需要回收的对象
- 并发清理:标记完成后,正式回收垃圾对象
- 并发重置:在垃圾回收完成后,重新初始化CMS数据结构和数据
- 当堆可用内存达到一定的阈值,收集器就会开始运行,以确保程序正常运行,阈值大小可以设定
优化技巧
- 带"并发"关键字的步骤会和程序一起抢夺CPU,如果CPU资源不够用,导致应用程序性能可能会非常糟糕
- 如果堆内存增长慢,我们可以将阈值设置大一些,可以有效的降低CMS回收频率
- 如果堆内存增长快,我们可以将阈值设置小一些,因为堆内存不够用后,会启动老年串行收集器,从而导致程序暂停
- 由于使用的标记 - 清除算法算法,多少次所以收集完成后,可以进行一次内存碎片整理
G1收集器
- 在吞吐量和停顿控制上,要由于CMS控制器
- 采用标记 - 压缩算法,因此不会产生空间碎片
- 可以精确的控制停顿时间,如下,在200ms内,停顿时间不超过50ms
-XX:MaxGCPauseMillis = 20
-XX:GCPauseIntervalMillis = 200