目录
- HotspotVM的垃圾收集器简介
- 1. Serial Collector
- 2. Parallel Collector(throughput collector)
- 3. Concurrent Mark Sweep Collector(CMS)
- 4. Garbage-First Garbage Collector(G1)
- 5. Z Garbage Collector(ZCG)JDK11
- JVM参数设置——选择垃圾收集器的组合
- 查看GC时用到的命令和JVM参数
- 查看本机默认JVM信息
- -XX:+UseSerialGC
- -XX:+UseParallelGC
- -XX:+UseParallelOldGC
- -XX:+UseParNewGC
- -XX:+UseConcMarkSweepGC
- -XX:+UseG1GC
- 总结
- 参考
【JVM学习笔记】JVM内存区域定义与内存结构
【JVM学习笔记】对象的创建过程、 对象的内存布局、 如何定位和使用对象
【JVM学习笔记】内存回收与内存回收算法 就哪些地方需要回收、什么时候回收、如何回收三个问题进行分析和说明
HotspotVM的垃圾收集器简介
1. Serial Collector
Serial收集器是最基础、历史最悠久的收集器。由一个线程处理所有垃圾收集工作,单线程避免了静态条件,可能会相对的提高工作效率。在单线程处理器,或多线程处理器中需要处理少量数据集(100MB左右)时,有比较明显的优势。
- Serail Young:Serial的新生代垃圾收集器,采用标记-复制算法 。
- Serial Old:Serial的老年代垃圾收集器,采用标记-整理算法。
2. Parallel Collector(throughput collector)
是Serial的多线程版,由多线程执行垃圾收集工作,适合在多处理器中处理中大型数据集,特别是在新生代并发执行时,能显著的减少垃圾收集器的开销。
- Parallel Young (ParNew):Parallel的新生代垃圾收集器,Serail Young的多线程版,它默认开启的收集线程数与处理器核心数量相同(-XX:ParallelGCThreads 可以限制收集线程数),侧重以多线程工作来降低暂停时长,采用标记-复制算法。
- Parallel Young (PSYoungGen或称Parallel Scavenge):Parallel的新生代垃圾收集器,与ParNew不同,它是一个以吞吐量优先的并发垃圾收集器,侧重于吞吐量的控制(控制最大垃圾收集停顿时间:-XX:MaxGCPauseMillis、设置吞吐量大小:-XX:GCTimeRatio),采用标记-复制算法。
- Parallel Old:Parallel的老年代垃圾收集器,Serial Old的多线程版,标记-整理算法。
3. Concurrent Mark Sweep Collector(CMS)
并发垃圾收集器,适合处理中大型数据集,甚至可以在程序允许时并发的处理大部分工作。这种收集器适用于需要更短暂停时长、对吞吐量要求不高的程序,不过它需要更多的处理器资源以快速处理垃圾收集工作。
- CMS:用于老年代,采用标记-清除算法。 优点:并发收集、低停顿;缺点:需要更多的处理器资源、产生大量空间碎片等。CMS默认启动的回收线程数是(处理器核心数量+3)/4,核心越多占用CPU越少,但是当CPU小于四个时,它占用的处理器资源也就越大。
4. Garbage-First Garbage Collector(G1)
G1是一种服务器端的并发垃圾收集器。与CMS相似,G1也适合处理中大型数据集,可以在程序允许时并发的处理大部分工作;不过G1收集器适用于具有大内存的多处理器机器,它在实现高吞吐量的同时,并能以高概率满足垃圾回收暂停时间目标。
- G1:虽然它的目标是取代CMS收集器,但其实G1是一个跨代的垃圾收集器,它将会收集所有区域的内存,因此G1无法与其它收集器组合。关于G1的内存结构可以参考这篇。
5. Z Garbage Collector(ZCG)JDK11
暂时没有研究,有空了再回来看
参考:
11 The Z Garbage Collector
An Introduction to ZGC: A Scalable and Experimental Low-Latency JVM Garbage Collector
JVM参数设置——选择垃圾收集器的组合
由于内存分代的出现,每种垃圾收集器,对于不同分代的垃圾回收会分别使用不同的垃圾收集技术。而虚拟机针对不同的现实条件,还可以选用垃圾收集器的部分垃圾收集功能,从而达到不同收集器的组合功能。垃圾收集器也提供了相应的组合VM参数,以达到不同收集器的组合实现。
关于如何启动不同组合的JVM参数,参考后面的内容。
需要注意,如果不显式的表明使用了哪种收集器,在不同的CPU硬件、操作系统下,会有不同的默认收集器。
查看GC时用到的命令和JVM参数
1. 查看当前机器上默认VM参数:
java -XX:+PrintCommandLineFlags -version
2. 查看指定Java运行程序的JVM参数(如果设置了JVM参数,将会覆盖上面的默认值)
jinfo -flags <pid>
查询指定参数(options)设置:jinfo -flag <options> <pid>
3. 通过jmap查看用了哪种算法的收集器
jmap -heap <pid>
Windows:jmap -heap <pid> | findstr GC
Linux:jmap -heap <pid> | grep GC
4. 通过jcmd强制gc
jcmd <pid> GC.run
注:可以通过jcmd <pid>help来查看程序可以执行的命令
5. 启用GC日志,方便查看GC时使用了什么收集器和收集时的内存信息(JDK8,JDK9后使用-Xlog参数打印日志)
-XX:+PrintGC:打印GC时的基本信息
-XX:+PrintGCDetails:打印GC详细信息
-XX:+PrintHeapAtGC:打印GC前后的堆、方法区可用容量的变化
-XX:+PrintGCTimeStamps**:打印GC时间戳
已经启动的Java程序,可以通过下面的命令启用上述参数:
jinfo -flag +PrintGC <pid>
查看本机默认JVM信息
本机配置信息:
内存:16GB
操作系统:WIN10 x64
JDK版本:1.8.0_171
VM:Java HotSpot™ 64-Bit Server VM
D:\Project>java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=257905728 -XX:MaxHeapSize=4126491648 -XX:+PrintCommandLineFlags
-XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation
-XX:+UseParallelGC
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
-XX:+UseSerialGC
启用组合:Serail Young (DefNew) + Serial Old Mark Sweep Compact
查看启用参数和GC内容:
D:\Project>jinfo -flag UseSerialGC 4288
-XX:+UseSerialGC
D:\Project>jmap -heap 4288 | findstr "GC"
Mark Sweep Compact GC
查看GC收集器详情:
D:\Project>jcmd 4288 GC.run
4288:
Command executed successfully
325.368: [Full GC (System.gc()) 325.368:
[Tenured: 32742K->32744K(349568K), 0.0769539 secs] 38309K->32744K(506816K),
[Metaspace: 57305K->57305K(1101824K)], 0.0770048 secs]
[Times: user=0.08 sys=0.00, real=0.08 secs]
Full GC (System.gc()):表明本次Full GC的原因是执行了System.gc()命令。
Tenured:老年代收集,一般表明老年代使用的是Serial Old收集器。
Metaspace:元空间收集情况,元空间为非堆,它是位于堆外的一块内存。
Times:本次GC所用时间。
-XX:+UseParallelGC
启用组合:Parallel Young (PSYoungGen、Parallel Scavenge) + Serial Old Mark Sweep Compact | Parallel Old Mark Sweep Compact
该JVM参数启用了Parallel Scavenge收集器,同时其老年代收集器默认为Parallel Old,可以通过设置参数来组合其它老年代的收集器。
查看启用参数和GC内容:
D:\Project>jinfo -flag UseParallelGC 15404
-XX:+UseParallelGC
D:\Project>jmap -heap 15404 | findstr "GC"
Parallel GC with 10 thread(s)
查看GC收集器详情:
D:\Project>jcmd 15404GC.run
15404:
Command executed successfully
76.217: [GC (System.gc())
[PSYoungGen: 2870K->288K(140288K)] 35650K->33075K(489984K), 0.0026375 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
76.220:
[Full GC (System.gc())
[PSYoungGen: 288K->0K(140288K)]
[ParOldGen: 32787K->31310K(349696K)] 33075K->31310K(489984K),
[Metaspace: 57241K->57241K(1101824K)], 0.0767850 secs]
[Times: user=0.22 sys=0.00, real=0.08 secs]
-XX:+UseParallelOldGC
启用组合:Serail Young (DefNew) | Parallel Young (PSYoungGen、Parallel Scavenge) | Parallel Young (ParNew)
+ Parallel Old Mark Sweep Compact
- JDK 6时才提供Parallel Old收集器。
- 该JVM参数,仅启用了Parallel Old收集器,新生代收集器会根据JVM默认参数来设置,也可以通过其它JVM参数指定新生代收集器。
- 我的机器默认使用了UseParallelGC参数,所以新生代为PSYoungGen。
查看启用参数和GC内容:
D:\Project>jinfo -flag UseParallelOldGC 4964
-XX:+UseParallelOldGC
D:\Project>jmap -heap 4964 | findstr "GC"
Parallel GC with 10 thread(s)
查看GC收集器详情:
D:\Project>jcmd 4964 GC.run
4964:
Command executed successfully
675.850: [GC (System.gc())
[PSYoungGen: 3065K->224K(141312K)] 29892K->27050K(491008K), 0.0026659 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
675.852: [Full GC (System.gc())
[PSYoungGen: 224K->0K(141312K)]
[ParOldGen: 26826K->26795K(349696K)] 27050K->26795K(491008K),
[Metaspace: 56938K->56938K(1101824K)], 0.1212485 secs]
[Times: user=0.27 sys=0.00, real=0.12 secs]
-XX:+UseParNewGC
启用组合:Parallel Young (ParNew)
+ Serial Old Mark Sweep Compact | Parallel Old Mark Sweep Compact | Concurrent Mark Sweep (Old)
- 除了Serial收集器外,目前只有它能与CMS收集器配合工作。
- ParNew + CMS与ParNew + Serial Old组合,在JDK8被宣布废弃,JDK9取消了对该组合的支持。
- -XX:+UseParNewGC参数也在JDK9后被直接取消了。
查看启用参数和GC内容:
D:\Project>jinfo -flag UseParNewGC 20052
-XX:+UseParNewGC
D:\Project>jmap -heap 20052 | findstr "GC"
Mark Sweep Compact GC
查看GC收集器详情(与serial别无二致,打印的GC日志都一样):
D:\Project>jcmd 20052 GC.run
20052:
Command executed successfully
16.495: [GC (Allocation Failure) 16.495:
[ParNew: 156622K->9790K(157248K), 0.0050190 secs] 213221K->66389K(506816K), 0.0050597 secs]
[Times: user=0.00 sys=0.00, real=0.00 secs]
89.628: [Full GC (System.gc()) 89.628:
[Tenured: 50767K->32723K(349568K), 0.0939450 secs] 83372K->32723K(506816K),
[Metaspace: 57211K->57211K(1101824K)], 0.0940025 secs]
[Times: user=0.09 sys=0.00, real=0.09 secs]
GC (Allocation Failure):表明本次GC的原因是内存分配失败导致的GC,从而进行的新生代垃圾收集(mirrorGC或称youngGC),如果GC后,内存仍无法分配就会尝试对堆进行扩容,如果已到堆的最大容量,那么将进行整堆收集。整堆收集后仍无法为对象分配内存时,就会抛出OOM异常。
-XX:+UseConcMarkSweepGC
启用组合:Serial Young (DefNew) | Parallel Young (ParNew) + Concurrent Mark Sweep (Old)
- CMS的默认新生代为ParNew。
- ParNew + CMS的组合在JDK8被宣布废弃,JDK9取消了对该组合的支持
查看启用参数和GC内容:
D:\Project>jinfo -flag UseConcMarkSweepGC 7156
-XX:+UseConcMarkSweepGC
D:\Project>jmap -heap 7156 | findstr "GC"
Concurrent Mark-Sweep GC
查看GC收集器详情:
D:\Project>jcmd 7156 GC.run
7156:
Command executed successfully
18.303: [GC (Allocation Failure) 18.303:
[ParNew: 153906K->13619K(157248K), 0.0094748 secs] 159600K->23724K(506816K), 0.0095149 secs]
[Times: user=0.02 sys=0.00, real=0.01 secs]
111.118: [Full GC (System.gc()) 111.118:
[CMS: 35564K->31991K(349568K), 0.0956757 secs] 37072K->31991K(506816K),
[Metaspace: 57246K->57246K(1101824K)], 0.0957642 secs]
[Times: user=0.09 sys=0.00, real=0.10 secs]
-XX:+UseG1GC
启用G1收集器
查看启用参数和GC内容:
D:\Project>jinfo -flag UseG1GC 14300
-XX:+UseG1GC
D:\Project>jmap -heap 14300 | findstr "GC"
Garbage-First (G1) GC with 10 thread(s)
查看GC收集器详情:
D:\Project>jcmd 14300 GC.run
14300:
Command executed successfully
91.976: [Full GC (System.gc()) 65M->31M(512M), 0.1022988 secs]
[Eden: 23.0M(284.0M)->0.0B(307.0M) Survivors: 23.0M->0.0B Heap: 65.8M(512.0M)->31.6M(512.0M)],
[Metaspace: 57227K->57206K(1101824K)]
[Times: user=0.11 sys=0.00, real=0.11 secs]
Eden:新生代收集情况。
Survivors:幸存区收集情况。
Heap:整个堆空间收集情况。
总结
这一篇博客只是介绍了实际环境中,可能用到哪些收集器,并对这些收集器的优劣、组合关系做了分析,同时也分享了一些实际运行环境中可能用到的命令和参数。最后对如何启用收集器的组合参数做了一些说明。
有关每种收集器的详细内容并未做过多的分析,因为每一种收集器涉及的知识量都比较广,如果对某种收集器有浓厚的兴趣,可以参考这官网的收集器说明,里面详细的介绍了每种收集器的实现原理和具体参数设置等,也可以参考周志明作的深入理解Java虚拟机一书。
参考
深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)周志明
java 8 doc
javase doc
Java HotSpot VM
memorymanagement
Java Virtual Machine Technology
Java Hotspot G1 GC的一些关键技术
Java HotSpot Equivalents of Exact VM Flags
Java Platform, Standard Edition Documentation
Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide