几种垃圾回收器:
一、Serial收集器
单线程收集器,对于新生代采用复制清除算法,对老年代使用标记整理算法,都需要stop the world。
在jdk1.3.1之前是hotspot的唯一收集器,由于是单线程收集器,因此效率低下,STW时间也比较长,但在单核或者处理器核心数较少的时候,由于其没有线程交互的开销,其反而比其他收集器效率高。
二、ParNew收集器
Serial收集器的多线程版本,专门收集新生代,除了使用多线程进行垃圾回收之外,其控制参数、回收策略都与Serial一样。
JDK5后,它一般与CMS共同使用,ParNew清理新生代,需要STW,CMS清理老年代,不需要STW。
JDK9之后,ParNew基本废弃,只与CMS合作使用。
它的默认收集线程数与处理器核心数相同。
三、Parallel Scavenge收集器
与ParNew的机制基本一致,专门收集新生代,特别之处在于其关注点是吞吐量而不是停顿时间,又被叫做吞吐量优先收集器。
JVM会根据系统运行情况动态调整Eden和Survior的大小和比例,通过设置-XX:GCTimeRatio来设置应用运行时间和GC运行时间的比值,默认为99:1
四、Serial Old收集器
Serial的老年代收集版本,单线程,专门收集老年代,采用标记整理算法,不严谨的说在JDK5之前它与Parallel Scavenge搭配使用;也作为CMS的收集失败后的预案使用。
五、Parallel Old收集器
Parallel Scavenge的老年代版本,支持多线程收集,使用标记整理算法,JDK6之后提供。
六、CMS收集器
尽可能减少STW的时间,采用标记清除算法,
收集过程为:初始标记,并发标记,重新标记,并发清除
初始标记:标记GCRoots,耗时很短,需要STW
并发标记:从GCRoots向下遍历所有关联的对象,耗时较长,不需要STW
重新标记:标记在并发标记过程中变化了的对象,耗时较短,需要STW
清除标记:清除掉被标记为死亡的对象,不需要STW
CMS虽然比较优秀,但仍有部分缺点:
1.在并发标记阶段它会跟应用线程并发跑,因此会降低部分吞吐量,同时它的默认线程数为(核数+3)/4,因此当处理器的核数低于4核时它的资源占用会较明显,核数越多,它的效率就越高。
2.CMS的标记清除注定会产生内存碎片,内存碎片足够多时,会导致没有连续空间分配大对象,导致提前FULL GC。
3.在标记结束后的短暂时间内,系统产生的垃圾对象只能等待下一次GC回收,因此CMS必须预留一部分老年代空间给垃圾对象,JDK5中老年代GC的阈值是占用率达到68%,JDK6调整到92%,如果CMS并发标记期间,内存地址已经不够分配了,那么会采用Serial Old的方式清除老年代,这将导致停顿时间较长,如果频繁出现这种情况,需要调整--XX:CMSInitiatingOccupancyFraction的大小,略微调小一点。
七、Garbage First收集器(G1)
主要目的是取代CMS和Parallel收集器,是一款面向服务器的垃圾回收器。
G1取消了Minor GC和Major GC的概念,以回收的收益为衡量标准进行垃圾回收。(并没有取消新生代老年代的概念)
G1将连续的Java堆划分为多个相等的独立区域Region,每个堆都可以扮演Eden、Survivor和Old空间。
对于比一个Region还大的大对象,G1的处理方式是为大对象提供内存地址连续的Region,将这种连续的Region区域称为Humongous Region,G1把它看作老年代。
对于跨Region的引用情况,G1为每个Region提供一个记忆集,记录别的Region指向自己的指针,这导致了G1的内存占用比其他垃圾收集器要高。
对于回收期间的分配对象问题,G1给每个Region设置了两个名为TAMS指针,并发回收期间分配的对象都在这两个指针位置以上,G1为这样的对象进行了隐形标记,避免在当前回收期间被误回收掉。
G1通过特殊的衰减平均值来计算出回收每个Region的预期花销,从而统筹安排回收计划。
收集器 | 运行时间 | 总停顿 | 最大停顿 | 最小停顿 |
Shenandoah | 387s | 0.32s | 0.089s | 0.053s |
G1 | 312s | 11.7s | 1.24s | 0.45s |
CMS | 285s | 12.78s | 4.39s | 0.852s |
Parellel | 260s | 6.59s | 3.04s | 0.823s |