这个组合的垃圾收集器使用标记-复制的方式清理年轻代,使用标记-清除-整理的方式清理老年代。它们都会触发Stop-the-world
暂停,挂起应用的全部线程。它们都会使用多线程来运行标记和复制/整理,这也是Parallel GC
名字的由来。使用这种方法,垃圾收集的次数会明显减少很多。
垃圾收集时使用的线程数可以通过参数-XX:ParallelGCThreads=NNN
设置,默认值为系统硬件的 CPU 核心数。
要使用Parallel GC
,可以选择下面三种方式之一来启动 JVM。
1
2
3
java -XX:+UseParallelGC com.mypackages.MyExecutableClass
java -XX:+UseParallelOldGC com.mypackages.MyExecutableClass
java -XX:+UseParallelGC -XX:+UseParallelOldGC com.mypackages.MyExecutableClass
在多核 CPU 环境下,如果你得首要目的是增加系统吞吐量,那么Parallel GC
非常合适。更高的吞吐量是由更高效的资源利用率带来的:
另一方面,因为垃圾收集的所有阶段都不能被打断,Parallel GC
还是有可能导致长时间的应用暂停。因此,如果低延时是你的首要目标,那么你应该去看看CMS 垃圾收集算法。
让我们来看看使用Parallel GC
时,垃圾收集日志长什么样,我们能从中得到哪些信息。下面的日志包含了 2 次垃圾收集事件:Minor GC
和Full GC
。
1
2
2015-05-26T14:27:40.915-0200: 116.115: [GC (Allocation Failure) [PSYoungGen: 2694440K->1305132K(2796544K)] 9556775K->8438926K(11185152K), 0.2406675 secs] [Times: user=1.77 sys=0.01, real=0.24 secs]
2015-05-26T14:27:41.155-0200: 116.356: [Full GC (Ergonomics) [PSYoungGen: 1305132K->0K(2796544K)] [ParOldGen: 7133794K->6597672K(8388608K)] 8438926K->6597672K(11185152K), [Metaspace: 6745K->6745K(1056768K)], 0.9158801 secs] [Times: user=4.49 sys=0.64, real=0.92 secs]
下面是第一段日志片段,包含了清理年轻代的垃圾收集事件信息:
2015-05-26T14:27:40.915-0200
:116.115
: [GC
(Allocation Failure
) [PSYoungGen
:2694440K->1305132K
(2796544K)
]9556775K->8438926K
(11185152K)
,0.2406675 secs
][Times: user=1.77 sys=0.01, real=0.24 secs]
2015-05-26T14:27:40.915-0200
– GC 开始时间。116.115
– GC 开始时间,相对于 JVM 的启动时间的偏移,单位秒。GC
– 标志位:Minor GC或Full GC。本次为Minor GC。Allocation Failure
– 触发垃圾收集的原因。本次为年轻代空间不足以分配新对象。PSYoungGen
– 使用的垃圾收集器名称,代表使用的是并行标记-复制且Stop-the-world
的垃圾收集器清理年轻代。2694440K->1305132K
– 垃圾收集前后年轻代空间使用量。(2796544K)
– 年轻代空间大小。9556775K->8438926K
– 垃圾收集前后堆上空间使用量。(11185152K)
– 堆大小。0.2406675 secs
– 垃圾收集时长,单位秒。[Times: user=1.77 sys=0.01, real=0.24 secs]
– 分类统计的垃圾收集时长:
- user:垃圾收集中的线程占用的 CPU 总时间
- sys:系统调用和等待系统事件占用的 CPU 时间
- real:应用暂停时长。使用
Paralled GC
时,,本次 GC 中使用了 8 个线程。要注意,GC 中总有一些操作是不能并行执行的,因此,实际的real
值一般会比计算出来的值大一些。
总之,垃圾收集之前堆的使用量为 9,556,775K,其中年轻代使用量为 2,694,440K,那么可算出老年代使用量为 6,862,335K。垃圾收集之后,年轻代使用量下降了 1,389,308K,但堆的使用量只下降了 1,117,849K,意味着有 271,459K的对象从年轻代提升到了老年代。
了解了上面清理年轻代的垃圾收集日志,下面通过分析第二段日志看看整个堆是怎么被清理的。
2015-05-26T14:27:41.155-0200
:116.356
: [Full GC
(Ergonomics
)[PSYoungGen: 1305132K->0K(2796544K)]
[ParOldGen
:7133794K->6597672K
(8388608K)
]8438926K->6597672K
(11185152K)
,[Metaspace: 6745K->6745K(1056768K)]
,0.9158801 secs
,[Times: user=4.49 sys=0.64, real=0.92 secs]
2015-05-26T14:27:41.155-0200
– GC 开始时间。116.356
– GC 开始时间,相对于 JVM 的启动时间的偏移,单位秒。Full GC
– 标志位:Minor GC
或Full GC
。本次为清理年轻代和老年代的Full GC
。Ergonomics
– GC 发生的原因。Ergonomics
表明 JVM 觉得是时候做些垃圾清理工作了。[PSYoungGen: 1305132K->0K(2796544K)]
– 跟上面的例子类似,它是一个叫PSYoungGen
的并发标记-复制且Stop-the-world
的垃圾收集器。年轻代的使用量降到了 0,这也是Full GC
的一般结果。ParOldGen
– 清理老年代的垃圾收集器类型。这是一个叫ParOldGen
的并行标记-清除-整理且Stop-the-world
的垃圾收集器。7133794K->6597672K
– 垃圾收集前后老年代使用量。(8388608K)
– 老年代空间大小。8438926K->6597672K
– 垃圾收集前后堆上空间使用量。(11185152K)
– 堆大小。[Metaspace: 6745K->6745K(1056768K)]
– 垃圾收集前后元数据区使用量。这里没有变化。0.9158801 secs
– 垃圾收集时长,单位秒。[Times: user=4.49 sys=0.64, real=0.92 secs]
– 分类统计的垃圾收集时长:
- user:垃圾收集中的线程占用的 CPU 总时间
- sys:系统调用和等待系统事件占用的 CPU 时间
- real:应用暂停时长。使用
Paralled GC
时,,本次 GC 中使用了 8 个线程。要注意,GC 中总有一些操作是不能并行执行的,因此,实际的real
值一般会比计算出来的值大一些。
Full GC跟Minor GC的区别很明显 – 除了年轻代,老年代和元数据区同样被清理了。下图示意了Full GC前后内存使用情况。