基于JDK1.7 Update 14之后的HotSpot虚拟机的垃圾收集器。

两个收集器之间有连线,说明他们可以搭配使用。收集器所处的区域,则表示他是属于新生代收集器还是老生代收集器。
Serial收集器


新生代收集(Minor GC)
下面代码尝试分配3个2MB大小和1个4MB大小的对象
- 运行通过-Xms20M(堆初始大小)、-Xmx20M(堆最大值)、-Xmn10M(新生代值)这3个参数限制了Java堆大小为20MB,不可扩展,其中10MB分配给新生代,剩下的10MB分配给老年代
- -XX:SurvivorRatio=8 决定了新生代中Eden区与一个Survivor区的空间比例为 8:1
- 新生代总可用空间:9216KB(Eden区+1个Survivor区的总容量)
- -XX:+PrintGCDetail,在发生垃圾收集时打印内存回收日志
1 | /** |
main方法执行后,在分配allocation4对象时,发生一次Minor GC,这次GC的结果是新生代7814K变为423K,而总内存占用量(7814K->6567K(19456K))几乎没有减少(因为allocation1、allocation2、allocation3三个对象都是存活的,虚拟机几乎没有找到可回收的对象)。
这次GC的原因是给allocation4分配所需的4MB内存的时候,发现Eden已经被占用了6MB,剩余空间已不足分配allocation4对象所需的4MB,因此发生Minor GC。
GC期间虚拟机又发现已有的3个2MB的对象无法全部放入Survivor空间(Survivor空间只有1MB大小),所以只好通过分配担保机制提前转移到老年代去。
这次GC结束后,4MB的allocation4对象顺利分配在Eden中,因此程序执行完成的结果是Eden占用4MB,Servivor空闲,老年代被占用6MB。
JDK1.6使用Serial收集器的GC日志:
1 | [GC (Allocation Failure) [DefNew: 7814K->423K(9216K), 0.0051201 secs] 7814K->6567K(19456K), 0.0051387 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] |
ParNew收集器
ParNew收集器其实就是Serial收集器的多线程版本,使用多条线程进行垃圾收集。


MinorGC示例使用ParNew收集器的GC日志:
JDK8虚拟机参数:
- -Xms20M
- -Xmx20M
- -Xmn10M
- -XX:+PrintGCDetails
- -XX:+UseParNewGC
1 | [GC (Allocation Failure) [ParNew: 7814K->467K(9216K), 0.0056367 secs] 7814K->6611K(19456K), 0.0056588 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] |
后面还会接触几款并行和并发的收集器。在谈垃圾收集的上下文语境下,可以解释如下:
- 并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
- 并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行在另一个cpu上
Parallel Scavenge收集器

Serial Old收集器


Parallel Old收集器
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。

MinorGC示例使用Parallel Scavenge + Parallel Old收集器的GC日志:
JDK8虚拟机参数:
- -Xms20M
- -Xmx20M
- -Xmn10M
- -XX:+PrintGCDetails
- -XX:+UseParallelOldGC
1 | Heap |
『ParOldGen total 10240K, used 4096K』看出allocation4直接被分配到老年代中。
CMS收集器


MinorGC示例使用ParNew + CMS + Serial Old(后备)收集器的GC日志:
JDK8虚拟机参数:
- -Xms20M
- -Xmx20M
- -Xmn10M
- -XX:+PrintGCDetails
- -XX:+UseConcMarkSweepGC
1 | [GC (Allocation Failure) [ParNew: 8144K->455K(9216K), 0.0054356 secs] 8144K->6601K(19456K), 0.0054911 secs] [Times: user=0.03 sys=0.00, real=0.00 secs] |
新生代使用的ParNew收集器,allocation4分配时新生代已没有足够的空间分配。Survivior只有1M不能够存放allocation1,2,3三个对象,所以这三个对象会复制到concurrent mark-sweep generation 中。最终allocation4分配到par new generation中。
从GC日志可以看出老年代GC的步骤:CMS-initial-mark -> CMS-concurrent-mark -> CMS-remark -> CMS-concurrent-sweep -> CMS-concurrent-reset(重置状态等待下一次触发GC)
G1收集器


垃圾收集器参数总结
垃圾收集相关的常用参数:
| 参数 | 描述 |
|---|---|
| UseSerialGC | 虚拟机运行在Client模式下的默认值,打开此开关后,使用 Serial + Serial Old的收集器组合进行内存回收 |
| UseParNewGC | 打开此开关后,使用ParNew + Serial Old的收集器组合进行内存回收 |
| UseConcMarkSweepGC | 打开此开关后,使用ParNew + CMS + Serial Old的收集器组合进行内存回收。Serial Old作为CMS出现Concurrent Mode Failure失败后的后备收集器使用 |
| UseParallelGC | 虚拟机运行在Server模式下的默认值,打开此开关后,使用Parallel Scanvenge + Serial Old (PS MarkSweep)的收集器组合进行内存回收 |
| UseParallelOldGC | 打开此开关后,使用 Parallel Scavenge + Parallel Old的收集器组合进行内存回收 |
| SurvivorRatio | 新生代中Eden区域与Survivor区域的容量比值,默认为8,代表Eden:Survivor = 8:1 |
| PretenureSizeThreshold | 直接晋级到老年代的对象大小,设置后,大于这个参数的对象将直接在老年代中分配 |
| MaxTenuringThreshold | 晋升到老年代的对象年龄。每个对象坚持过一次Minor GC之后,年龄就增加1,当超过这个参数值以后就进入老年代 |
| UseAdaptiveSizePolicy | 动态调整Java堆中各个区域的大小以及进入老年代的年龄 |
| HandlePromotionFailure | 是否允许担保失败,即老年代的剩余空间不足以应付新生代的整个Eden和Survivor区所有对象都存活的极端情况 |
| ParallelGCThreads | 设置并行GC时进行内存回收的线程数 |
| GCTimeRatio | GC时间占总时间的比率,默认值99,即允许1%的GC时间。仅在使用Parallel Scavenge收集器时生效 |
| MaxGCPauseMillis | 设置GC的最大停顿时间。仅在使用Parallel Scavenge收集器时生效 |
| CMSInitiatingOccupancyFraction | 设置CMS收集器在老年代空间被使用多少后触发垃圾收集。默认值为68%,仅在CMS收集器时生效 |
| UseCMSCompactAtFullCollection | 设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片整理。仅在CMS收集器时生效 |
| CMSFullGCsBeforeCompaction | 设置CMS收集器在进行若干次垃圾收集后再启动一次碎片整理。仅在CMS收集器时生效 |
