现代JVM的类型繁多,最主流的四个垃圾收集器Serial收集器、 Parallel / Throughput收集器、Concurrent收集器(CMS)和G1收集器。这四种垃圾收集器各有各的特点,需要我们根据自己的实际应用场景选择合适的垃圾收集器。虽然存在差异但是它们也有很多共性:1)所有的垃圾收集器都将堆划分为老年代和新生代(分代收集)2)所有的垃圾收集器在清理新生代对象时候,都使用了(stop-the-world)方式的垃圾收集方法。本文也是主要对着这四种垃圾收集器进行简单的介绍。
Serial收集器是一个单线程的收集器,使用Serial收集器时候无论是进行Minor GC,还是进行Full GC ,清理堆空间时,所有的应用线程都会被暂停。进行Full GC时候,它还会对老年代对象进行压缩整理。其比较适合于单核处理器(因为是单线程不能充分利用多核处理器硬件);启用方式:-XX:+UseSerialGC(打开此开关后,使用Serial + Serial Old的收集器组合进行内存回收)。
public class CollectorTest {
private static final int _1MB = 1024 * 1024;
/**
*-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+UseSerialGC
*
*
* @param args
*/
public static void main(String[] args) throws Exception {
byte[] bytes1 = new byte[5*_1MB];
byte[] bytes3 = new byte[5* _1MB];
}
}
可以看出,DefNew表示使用Serial收集器。
[GC (Allocation Failure) [DefNew: 7182K->402K(9216K), 0.0056138 secs] 7182K->5522K(19456K), 0.0056532 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
Heap
def new generation total 9216K, used 5689K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
eden space 8192K, 64% used [0x00000007bec00000, 0x00000007bf1299e0, 0x00000007bf400000)
from space 1024K, 39% used [0x00000007bf500000, 0x00000007bf564a60, 0x00000007bf600000)
to space 1024K, 0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000)
tenured generation total 10240K, used 5120K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
the space 10240K, 50% used [0x00000007bf600000, 0x00000007bfb00010, 0x00000007bfb00200, 0x00000007c0000000)
Metaspace used 3298K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 368K, capacity 388K, committed 512K, reserved 1048576K
Parallel 收集器也成为Throughput收集器,它是Server级虚拟机的默认收集器。Parallel 收集器是Serial收集器多线程版。因此在多核处理器要比Serial收集器快很多。Throughput收集器在Minor GC 和Full GC 时候会暂停所有的应用线程,同时在Full GC时候,它还会对老年代对象进行压缩整理。启用方式:-XX:+UseParallelGC(开启此参数使用parallel scavenge & parallel old搜集器),-XX:+UseParallelOldGC。
- Parallel Scavenge收集器,新生代收集使用,其可控制吞吐量(与ParNew收集器的唯一区别)。
- Parallel Old收集器,老年代收集使用。
public class CollectorTest {
private static final int _1MB = 1024 * 1024;
/**
* -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:-UseParallelGC
*
* @param args
*/
public static void main(String[] args) throws Exception {
byte[] bytes1 = new byte[4* _1MB];
byte[] bytes2 = new byte[4 * _1MB];
byte[] bytes3 = new byte[4 * _1MB];
bytes2=null;
byte[] bytes4 = new byte[2* _1MB];
}
}
可以看出PSYoungGen、ParOldGen为 Parallel收集器。
[GC (Allocation Failure) --[PSYoungGen: 6158K->6158K(9216K)] 14350K->14358K(19456K), 0.0014680 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 6158K->0K(9216K)] [ParOldGen: 8200K->8618K(10240K)] 14358K->8618K(19456K), [Metaspace: 3306K->3306K(1056768K)], 0.0064933 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 9216K, used 2130K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
eden space 8192K, 26% used [0x00000007bf600000,0x00000007bf814930,0x00000007bfe00000)
from space 1024K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007c0000000)
to space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
ParOldGen total 10240K, used 8618K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
object space 10240K, 84% used [0x00000007bec00000,0x00000007bf46ab60,0x00000007bf600000)
Metaspace used 3313K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 369K, capacity 388K, committed 512K, reserved 1048576K
CMS 收集器设计的初衷是为了消除Parallel收集器和Serial收集器Full GC 周期中的长时间的停顿。CMS收集器在Minor GC时会暂停所有的应用线程,并以多线程的方式进行垃圾回收,CMS不在使用Parallel的收集算法,改用新的算法(ParNew)来收集新生代。CMS 收集器在Full GC 时候不在暂停应用线程,而是使用若干个后台线程定期对老年代空间进行扫描,及时回收其中不在使用的对象。CMS收集器优点是并发收集、低停顿。缺点容易产生碎片(标记清除算法)、占用CPU资源。启用方式: -XX:+UseConcMarkSweepGC(使用ParNew & CMS,serial old为替补)。
public class CollectorTest {
private static final int _1MB = 1024 * 1024;
/**
* -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC
*
* @param args
*/
public static void main(String[] args) throws Exception {
byte[] bytes1 = new byte[4* _1MB];
byte[] bytes2 = new byte[4 * _1MB];
byte[] bytes3 = new byte[4 * _1MB];
bytes2=null;
byte[] bytes4 = new byte[2* _1MB];
}
}
[GC (Allocation Failure) [ParNew: 6159K->419K(9216K), 0.0089901 secs] 6159K->4517K(19456K), 0.0090417 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [ParNew: 4599K->502K(9216K), 0.0038158 secs] 8697K->8696K(19456K), 0.0038497 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (CMS Initial Mark) [1 CMS-initial-mark: 8194K(10240K)] 15133K(19456K), 0.0010468 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[CMS-concurrent-abortable-preclean-start]
[CMS-concurrent-abortable-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 9216K, used 6938K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
eden space 8192K, 78% used [0x00000007bec00000, 0x00000007bf249058, 0x00000007bf400000)
from space 1024K, 49% used [0x00000007bf400000, 0x00000007bf47dba0, 0x00000007bf500000)
to space 1024K, 0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)
concurrent mark-sweep generation total 10240K, used 8194K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
Metaspace used 3290K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 365K, capacity 388K, committed 512K, reserved 1048576K
[GC (CMS Final Remark) [YG occupancy: 6938 K (9216 K)][Rescan (parallel) , 0.0008426 secs][weak refs processing, 0.0000090 secs][class unloading, 0.0003863 secs][scrub symbol table, 0.0008789 secs][scrub string table, 0.0001825 secs][1 CMS-remark: 8194K(10240K)] 15133K(19456K), 0.0023860 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
G1收集器属于Concurrent收集器,用于大堆区域;新生代的垃圾收集依然采用暂停应用线程的方式,老年代的垃圾收集工作由后台线程完成。由于G1收集器基于Mark-Compact算法,划分多个大小固定的区域,不会产生碎片。启用方式: -XX:+UseG1GC 。
public class CollectorTest {
private static final int _1MB = 1024 * 1024;
/**
* -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+UseG1GC
*
* @param args
*/
public static void main(String[] args) throws Exception {
byte[] bytes1 = new byte[4* _1MB];
byte[] bytes2 = new byte[4 * _1MB];
byte[] bytes3 = new byte[4 * _1MB];
bytes2=null;
byte[] bytes4 = new byte[2* _1MB];
}
}
[GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0017483 secs]
[Parallel Time: 1.2 ms, GC Workers: 4]
[GC Worker Start (ms): Min: 346.0, Avg: 346.0, Max: 346.0, Diff: 0.0]
[Ext Root Scanning (ms): Min: 0.6, Avg: 0.6, Max: 0.6, Diff: 0.0, Sum: 2.3]
[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Object Copy (ms): Min: 0.5, Avg: 0.5, Max: 0.6, Diff: 0.0, Sum: 2.2]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Termination Attempts: Min: 1, Avg: 1.8, Max: 4, Diff: 3, Sum: 7]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
[GC Worker Total (ms): Min: 1.1, Avg: 1.1, Max: 1.2, Diff: 0.0, Sum: 4.6]
[GC Worker End (ms): Min: 347.1, Avg: 347.1, Max: 347.1, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.2 ms]
[Other: 0.3 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.1 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.0 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 2048.0K(10.0M)->0.0B(9216.0K) Survivors: 0.0B->1024.0K Heap: 6144.0K(20.0M)->4580.9K(20.0M)]
[Times: user=0.00 sys=0.00, real=0.01 secs]
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0007369 secs]
[GC concurrent-mark-start]
[GC concurrent-mark-end, 0.0000357 secs]
[GC remark [Finalize Marking, 0.0000582 secs] [GC ref-proc, 0.0000332 secs] [Unloading, 0.0006383 secs], 0.0008605 secs]
[Times: user=0.01 sys=0.00, real=0.00 secs]
[GC cleanup 12M->12M(20M), 0.0005868 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
[GC pause (G1 Humongous Allocation) (young), 0.0010836 secs]
[Parallel Time: 0.6 ms, GC Workers: 4]
[GC Worker Start (ms): Min: 356.2, Avg: 356.2, Max: 356.2, Diff: 0.0]
[Ext Root Scanning (ms): Min: 0.2, Avg: 0.2, Max: 0.3, Diff: 0.1, Sum: 0.8]
[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Object Copy (ms): Min: 0.3, Avg: 0.3, Max: 0.4, Diff: 0.1, Sum: 1.4]
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Termination Attempts: Min: 1, Avg: 1.8, Max: 3, Diff: 2, Sum: 7]
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[GC Worker Total (ms): Min: 0.6, Avg: 0.6, Max: 0.6, Diff: 0.0, Sum: 2.3]
[GC Worker End (ms): Min: 356.8, Avg: 356.8, Max: 356.8, Diff: 0.0]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.0 ms]
[Other: 0.4 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.1 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.0 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.3 ms]
[Eden: 1024.0K(9216.0K)->0.0B(9216.0K) Survivors: 1024.0K->1024.0K Heap: 12.7M(20.0M)->8834.5K(20.0M)]
[Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
garbage-first heap total 20480K, used 10882K [0x00000007bec00000, 0x00000007bed000a0, 0x00000007c0000000)
region size 1024K, 2 young (2048K), 1 survivors (1024K)
Metaspace used 3313K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 369K, capacity 388K, committed 512K, reserved 1048576K
参考文献
[1] 深入理解java 虚拟机(第二版),周志明著
[2] java 性能权威指南,Scott Oaks著