原创

虚拟机垃圾收集器

JVM 常见垃圾回收器详解

Hotspot JVM 1.6 的垃圾收集器种类示意图 (图中两个收集器之间有连线,说明它们可以配合使用)

垃圾收集器种类示意图
垃圾收集器种类示意图

Serial 垃圾收集器:

单线程的垃圾收集器,适用于新生代。在触发该收集器时,必须暂停其他所有工作线程(该事件被 SUN 成为“Stop The World”),直到它收集结束,“Stop The World”会给用户代码很不好的用户体验。

ParNew 垃圾收集器:

Serial 收集器的多线程版本,除了使用多个线程进行垃圾回收外,其他行为和 Serial 收集器完全一样,适用于新生代。

Parallel Scavenge 垃圾收集器:

该收集器是使用复制算法、适用于新生代的垃圾收集器,其目的是达到一个可控制的吞吐量(Throughput),从而最高效的利用 CPU 时间,尽快的完成任务,主要适合在后台运算而不需要太多交互的任务场景。

Parallel Scavenge 垃圾收集器主要提供两个参数用于精确控制吞吐量,分别是 控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis和直接设置吞吐量大小的 -XX:GCTimeRatio。 除上述两个参数外,还有一个开关参数-XX:+UseAdaptiveSizePolicy,当设置这个参数后,既不需要手工指定新生代的大小等参数了,虚拟机会根据当前系统的允许情况动态调整这些参数。 由于与吞吐量关系密切,Parallel Scavenge 也被称为“吞吐量优先”的垃圾收集器。 Serial Old 垃圾收集器:老年代的单线程垃圾收集器。另外一个作用就是在 CMS 并发收集器发生 Concurrent Mode Failure 时使用。

Parallel Old 垃圾收集器:

是 Parallel Scavenge 收集器的老年代的版本,使用多线程和“标记-整理”算法。该收集器是在 JDK1.6 中才有的。

在注重吞吐量和 CPU 资源敏感的场景中,应首选 Parallel Scavenge + Parallel Old 的组合。

CMS 垃圾收集器:

是以“用户响应优先”的并发垃圾收集器,基于“标记-清楚”算法实现的。 该收集器执行垃圾回收时不会暂停其他工作线程,即不会发生“Stop The World”,它每次只清除部分垃圾;由于 CMS 是基于“标记—清除”算法实现的,故会产生大量内存碎片,这会给大对象分配带来大麻烦;于是 CMS 提供了一个-XX:+UseCMSCompactAtFullCollection的开关参数,该参数会在每次触发一次 Full GC 后执行一个碎片整理操作,内存整理无法并发操作,于是空间碎片没有了但停顿时间不得不变长,然后就有了另外一个参数-XX:CMSFullGCsBeforeCompaction,用于设置执行多少次不压缩的 Full GC 后执行一次带压缩的 Full GC。

在注重用户响应体验的场景中,应首选 ParNew + CMS 的组合。

G1 收集器

为解决 CMS 算法产生空间碎片和其它一系列的问题缺陷,HotSpot 提供了另外一种垃圾回收策略,G1(Garbage First)算法,通过参数-XX:+UseG1GC来启用,该算法在 JDK 7u4 版本被正式推出,G1 垃圾收集算法主要应用在多 CPU 大内存的服务中,在满足高吞吐量的同时,竟可能的满足垃圾回收时的暂停时间,该设计主要针对如下应用场景:

  • 垃圾收集线程和应用线程并发执行,和 CMS 一样
  • 空闲内存压缩时避免冗长的暂停时间
  • 应用需要更多可预测的 GC 暂停时间
  • 不希望牺牲太多的吞吐性能

使用技巧:

较小堆引起的碎片问题

因为老代年的并发收集器使用标记,清除算法,所以不会对堆进行压缩.当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象.但是,当堆空间较小时,运行一段时间以后,就会出现"碎片",如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记,清除方式进行回收.如果出现"碎片",可能需要进行如下配置:

-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩.

-XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次 Full GC 后,对年老代进行压缩

XMX 和 XMS 设置一样大

MaxPermSize 和 MinPermSize 设置一样大,这样可以减轻伸缩堆大小带来的压力

使用 CMS 的好处

用尽量少的新生代,经验值是 128M-256M,然后老生代利用 CMS 并行收集,实际上 cms 的收集停顿时间非常的短,2G 的内存, 大约 20-80ms 的应用程序停顿时间

多使用 jstack,jmap

系统停顿的时候可能是 GC 的问题也可能是程序的问题,多用 jmap 和 jstack 查看,然后查看 java 控制台日志,能看出很多问题。

缓存使用技巧

仔细了解自己的应用,如果用了缓存,那么年老代应该大一些,缓存的 HashMap 不应该无限制长,建议采用 LRU 算法的 Map 做缓存,LRUMap 的最大长度也要根据实际情况设定。

正文到此结束