文章目录
  1. 1. GC参数介绍
    1. 1.1. 串行收集器
    2. 1.2. 并行收集器
      1. 1.2.1. ParNew
      2. 1.2.2. Parallel收集器
      3. 1.2.3. 并行收集器运行参数控制
    3. 1.3. CMS收集器
      1. 1.3.1. CMS收集器缺点
      2. 1.3.2. CMS收集器内存碎片问题

GC参数介绍

在本文中将介绍 串行收集器 并行收集器 CMS收集器 还会有一个tomcat的实例演示

串行收集器

串行收集器的特点:

  • 最古老、最稳定
  • 效率高
  • 可能会产生较长的停顿
  • -XX:+UseSerialGC
    • 新生代、老年代使用串行回收
    • 新生代复制算法
    • 老年代标记-压缩算法

串行GC运行流程和GC日志:

串行GC运行流程

新生代GC日志:

1
0.844: [GC 0.844: [DefNew: 17472K->2176K(19648K), 0.0188339 secs] 17472K->2375K(63360K), 0.0189186 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]

老年代GC日志:

1
8.259: [Full GC 8.259: [Tenured: 43711K->40302K(43712K), 0.2960477 secs] 63350K->40302K(63360K), [Perm : 17836K->17836K(32768K)], 0.2961554 secs] [Times: user=0.28 sys=0.02, real=0.30 secs]

并行收集器

ParNew

新生代并行收集,老年代还是串行,通过 -XX:+UseParNewGC 指定使用。在新生代还是使用复制算法,需要多核支持。通过 -XX:ParallelGCThreads 限制线程数量

并行收集-ParNew流程

新生代GC日志

1
2
3
4
[GC [ParNew: 2276K->124K(3072K), 0.0019210 secs] 2276K->1148K(9920K), 0.0019460 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [ParNew: 2202K->150K(3072K), 0.0013780 secs] 3226K->2198K(9920K), 0.0013970 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [ParNew: 2273K->154K(3072K), 0.0015180 secs] 4321K->3226K(9920K), 0.0015420 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [ParNew: 2215K->155K(3072K), 0.0011620 secs] 5287K->4251K(9920K), 0.0011810 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

老年代和Serial收集器一样

Parallel收集器

  • 类似于ParNew
  • 新生代 复制算法
  • 老年代 标记-压缩法
  • 更加关注吞吐量
  • -XX:+UseParallelGC
    • 使用Parallel收集器+老年代串行
  • -XX:+UseParallelOldGC
    • 使用Parallel收集器+老年代并行

Parallel收集器示例图:

Parallel收集器示例图

GC日志示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
java -Xmx7m -Xms7m -XX:+UseParallelGC -XX:+PrintGCDetails TestDump2
[GC [PSYoungGen: 1253K->192K(2368K)] 6373K->5312K(7872K), 0.0009380 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC-- [PSYoungGen: 1216K->1216K(2368K)] 6336K->6336K(7872K), 0.0005970 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC [PSYoungGen: 1216K->0K(2368K)] [PSOldGen: 5120K->1146K(4800K)] 6336K->1146K(7168K) [PSPermGen: 2571K->2571K(21248K)], 0.0031580 secs] [Times: user=0.00 sys=0.01, real=0.00 secs]
Heap
PSYoungGen total 2368K, used 1106K [0x00000000ffd60000, 0x0000000100000000, 0x0000000100000000)
eden space 2048K, 54% used [0x00000000ffd60000,0x00000000ffe74888,0x00000000fff60000)
from space 320K, 0% used [0x00000000fff60000,0x00000000fff60000,0x00000000fffb0000)
to space 320K, 0% used [0x00000000fffb0000,0x00000000fffb0000,0x0000000100000000)
PSOldGen total 4800K, used 3194K [0x00000000ff800000, 0x00000000ffcb0000, 0x00000000ffd60000)
object space 4800K, 66% used [0x00000000ff800000,0x00000000ffb1ebc0,0x00000000ffcb0000)
PSPermGen total 21248K, used 2583K [0x00000000fa600000, 0x00000000fbac0000, 0x00000000ff800000)
object space 21248K, 12% used [0x00000000fa600000,0x00000000fa885fb8,0x00000000fbac0000)

并行收集器运行参数控制

  • -XX:MaxGCPauseMills
    • 最大停顿时间,单位毫秒
    • GC尽力保证回收时间不超过设定值
  • -XX:GCTimeRatio
    • 0-100的取值范围
    • 垃圾收集时间占总时间的比
    • 默认99,即最大允许1%时间做GC
  • 这两个参数是矛盾的。因为停顿时间和吞吐量不可能同时调优

CMS收集器

CMS(Concurrent Mark Sweep)收集器,并发(与用户线程一起执行)标记清除

  • 使用标记-清除算法
  • 并发阶段会降低吞吐量
  • 老年代收集器(新生代使用ParNew)
  • -XX:+UseConcMarkSweepGC

CMS运行过程比较复杂,着重实现了标记的过程,具体分为:

  • 初始标记(独占CPU)
    • 该阶段仅仅只是标记一下GC Roots能直接关联到的对象。
    • 速度快
  • 并发标记(和用户线程一起)
    • 并发标记阶段就是进行GC Roots Tracing 的过程
  • 重新标记(独占CPU)
    • 重新标记阶段是为了修正并发标记期间因用户程序继续运行导致标记产生变动的那一部分对象的标记记录
    • 该阶段停止时间一般比初识标记长些,但远比并发标记时间短
  • 并发清除(和用户线程一起)
    • 基于标记结果,直接清理对象

CMS收集器图例:

CMS收集器图例

从上面的图例可以看出,CMS收集器器也不是绝对的不暂停用户线程,在某些阶段还是需要暂停用户线程的,比如在初始化标记和重新标记阶段,用户线程是暂停的。CMS使用的是标记-清除算法,这是没有使用标记-压缩算法是因为清理的阶段是和用户线程一起执行的。

CMSGC日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
java -Xmx40m -Xms40M -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails TestDump2
[GC [ParNew: 15700K->132K(19136K), 0.0069140 secs] 15700K->5254K(38848K), 0.0069660 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[GC [ParNew: 16006K->150K(19136K), 0.0032970 secs] 21128K->10392K(38848K), 0.0033360 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC [1 CMS-initial-mark: 10242K(19712K)] 15512K(38848K), 0.0004370 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [ParNew: 15624K->154K(19136K), 0.0025730 secs] 25866K->15517K(38848K), 0.0026140 secs] [Times: user=0.00 sys=0.01, real=0.00 secs]
[CMS-concurrent-mark: 0.004/0.007 secs] [Times: user=0.01 sys=0.01, real=0.00 secs]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-abortable-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC[YG occupancy: 10470 K (19136 K)][Rescan (parallel) , 0.0003380 secs][weak refs processing, 0.0000050 secs] [1 CMS-remark: 15362K(19712K)] 25832K(38848K), 0.0004400 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [ParNew: 15590K->140K(19136K), 0.0020320 secs] 25832K->15502K(38848K), 0.0022380 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[CMS-concurrent-reset: 0.008/0.011 secs] [Times: user=0.01 sys=0.01, real=0.01 secs]
[GC [ParNew: 15549K->15549K(19136K), 0.0000270 secs][CMS: 15362K->5243K(19712K), 0.0118990 secs] 30911K->5243K(38848K), [CMS Perm : 2576K->2576K(21248K)], 0.0119780 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[GC [ParNew: 15392K->0K(19136K), 0.0015490 secs] 20636K->10365K(38848K), 0.0016510 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [1 CMS-initial-mark: 10365K(19712K)] 15485K(38848K), 0.0000900 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-mark: 0.004/0.004 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-abortable-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC[YG occupancy: 10602 K (19136 K)][Rescan (parallel) , 0.0013350 secs][weak refs processing, 0.0000150 secs] [1 CMS-remark: 10365K(19712K)] 20967K(38848K), 0.0018070 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 19136K, used 10602K [0x00000000f8600000, 0x00000000f9ac0000, 0x00000000f9ac0000)
eden space 17024K, 62% used [0x00000000f8600000, 0x00000000f905a810, 0x00000000f96a0000)
from space 2112K, 0% used [0x00000000f98b0000, 0x00000000f98b0000, 0x00000000f9ac0000)
to space 2112K, 0% used [0x00000000f96a0000, 0x00000000f96a0000, 0x00000000f98b0000)
concurrent mark-sweep generation total 19712K, used 5245K [0x00000000f9ac0000, 0x00000000fae00000, 0x00000000fae00000)
concurrent-mark-sweep perm gen total 21248K, used 2584K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)

通过上面的GC日志可以看出该次执行,发生了两次Full GC,主要关注:

  • CMS-initial-mark 初始标记
  • CMS-concurrent-mark 并发标记
  • CMS-remark 重新标记
  • CMS-concurrent-sweep 并发清理

和上面介绍的CMS流程一致

CMS收集器缺点

  • CMS收集器对CPU资源非常敏感。
  • CMS收集器无法处理浮动垃圾。由于CMS并发清理阶段用户线程还在运行着,伴随程序运行必然会有新的垃圾对象产生,这一部分垃圾因为在标记过程之后,CMS无法在当次收集中处理掉它们,只好留到下次GC时清理,这一部分垃圾就是浮动垃圾
  • CMS收集因为是基于“标记-清除”算法,会产生大量碎片

CMS收集器内存碎片问题

因为CMS收集器使用标记-清除算法,必然会导致出现内存碎片问题。如果不进行内存碎片整理,则无法放置较大对象。为了解决该问题,CMS收集器提供了如下参数进行调控:

  • -XX:+UseCMSCompactAtFullCollection
    • Full GC后进行一次碎片整理,整理过程是独占的,会引起停顿时间变长
  • -XX:+CMSFullGCsBeforeCompaction
    • 设置进行几次Full GC后,进行一次碎片整理
  • -XX:ParallelCMSThreads
    • 设置CMS的线程数量
文章目录
  1. 1. GC参数介绍
    1. 1.1. 串行收集器
    2. 1.2. 并行收集器
      1. 1.2.1. ParNew
      2. 1.2.2. Parallel收集器
      3. 1.2.3. 并行收集器运行参数控制
    3. 1.3. CMS收集器
      1. 1.3.1. CMS收集器缺点
      2. 1.3.2. CMS收集器内存碎片问题