一、垃圾回收器种类

1. 串行

  • 单线程
  • 堆内存较小,适合个人电脑

使用参数:-XX:+UseSerialGC(复制算法) = Serial + SerialOld(标记整理算法)

工作过程:

2. 吞吐量优先

  • 多线程
  • 堆内存较大,多核 cpu
  • 让单位时间内,STW 的时间最短 0.2 0.2 = 0.4,垃圾回收时间占比最低,这样就称吞吐量高

使用参数:-XX:+UseParallelGC ~ -XX:+UseParallelOldGC  (jdk1.8默认使用的垃圾回收期

-XX:ParallelGCThreads=n:并发垃圾回收线程数量,默认与cpu核实相同

-XX:+UseAdaptiveSizePolicy:动态分配新生代Eden与s0、s1分配比例

-XX:GCTimeRatio=ratio:调整垃圾回收时间与工作总时间占比,比值为1/(1+ratio),ratio越大,垃圾回收频率越小,默认99,一般设为19
-XX:MaxGCPauseMillis=ms:GC最大暂停毫秒数,默认200,与上述参数对立

工作过程:

3. 响应时间优先(CMS jdk 1.9废弃)

  • 多线程
  • 堆内存较大,多核 cpu
  • 尽可能让单次 STW 的时间最短 0.1 0.1 0.1 0.1 0.1 = 0.5

使用参数-XX:+UseConcMarkSweepGC(标记清除算法) ~ -XX:+UseParNewGC ~ SerialOld 老年代当内存碎片过多,内存不足造成并发失败会退化使用串行垃圾回收器
-XX:ParallelGCThreads=n(并行线程数,包含用户线程与垃圾回收线程) ~ -XX:ConcGCThreads=threads(用于垃圾回收的并发线程,一般为ParallelGCThreads的1/4)
-XX:CMSInitiatingOccupancyFraction=percent 垃圾回收时的内存占比,用于垃圾回收时产生的浮动垃圾的回收
-XX:+CMSScavengeBeforeRemark 重新标记前对新生代做一次垃圾回收

二、G1垃圾回收器

 

三、垃圾回收调优

1、调优领域
内存、锁竞争、cpu 占用、io

2、确定目标(【低延迟】还是【高吞吐量】,选择合适的回收器)

CMS,G1,ZGC(低延迟)
ParallelGC(高吞吐量)
Zing(jdk12推出试用版回收器)

3、最快的 GC(答案是不发生 GC)

查看 FullGC 前后的内存占用,考虑下面几个问题

数据是不是太多?resultSet = statement.executeQuery("select * from 大表 limit n")

数据表示是否太臃肿?对象图、对象大小 16 Integer 24 int 4

是否存在内存泄漏?static Map map =  ? 软引用、弱引用、第三方缓存实现

4、新生代调优

新生代的特点

  • 所有的 new 操作的内存分配非常廉价   (TLAB thread-local allocation buffer)
  • 死亡对象的回收代价是零
  • 大部分对象用过即死
  • Minor GC 的时间远远低于 Full GC

越大越好吗?

  • 新生代能容纳所有【并发量 * (请求-响应)】的数据
  • 幸存区大到能保留【当前活跃对象+需要晋升对象】
  • 晋升阈值配置得当,让长时间存活对象尽快晋升

-XX:MaxTenuringThreshold=threshold(晋升阈值设置,默认15)
-XX:+PrintTenuringDistribution(晋升阈值打印)
Desired survivor size 48286924 bytes, new threshold 10 (max 10)
- age 1: 28992024 bytes, 28992024 total
- age 2: 1366864 bytes, 30358888 total
- age 3: 1425912 bytes, 31784800 total

5、老年代调优

以 CMS 为例

  • CMS 的老年代内存越大越好(否则产生浮动垃圾,导致回退到串行垃圾回收器,效率较低)
  • 先尝试不做调优,如果没有 Full GC 那么已经...,否则先尝试调优新生代
  • 观察发生 Full GC 时老年代内存占用,将老年代内存预设调大 1/4 ~ 1/3
  • -XX:CMSInitiatingOccupancyFraction=percent 老年代触发垃圾回收内存百分比