HotSpot JVM收集器
上面有7中收集器,分为两块,上面为新生代收集器,下面是老年代收集器。如果两个收集器之间存在连线,就说明它们可以搭配使用。
收集器
|
|
回收区域
|
特性
|
回收算法
|
使用场景
|
---|---|---|---|---|---|
Serial(串行GC) | -XX:+UseSerialGC | 新生代 | 历史最悠久 单线程,进行垃圾收集时,必须暂停其他所有工作线程“Stop The World” 简单、高效,无线程切换开销 | 复制算法 | JVM Client模式下默认的新生代收集器, 桌面应用 |
ParNew(并行GC) | -XX:+UseParNewGC -XX:+UseConcMarkSweepGC | 新生代 | 多线程收集,默认开启收集线程数与CPU的数量相同, -XX: ParallelGCThreads,限制垃圾收集线程数 | 复制算法 | Server模式下新生代收集器,首要选择 |
Parallel Scavenge (并行回收GC) | -XX:+UseParallelGC(Parallel Scavenge + Serial Old) -XX:+UseParallelOldGC( Parallel Scavenge + Parallel Old)
| 新生代 | 吞吐量优先: 它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。 吞吐量= 程序运行时间/(程序运行时间 + 垃圾收集时间) 虚拟机总共运行了100分钟。其中垃圾收集花掉1分钟,那吞吐量就是99%
提供两个精确控制吞吐量的参数: -XX:MaxGCPauseMills 控制最大垃圾收集停顿时间, -XX:GCTimeRatio 设置吞吐量大小,默认值99
-XX:+UseAdaptiveSizePolicy 自适应调节,是Parallel Scavenge和ParNew的重要区别 | 复制算法 | 后台运算,不需要太多交互的任务 |
Serial Old(串行GC) | -XX:+UseParallelGC(Parallel Scavenge + Serial Old) | 老年代 | 单线程 与Parallel Scavenge配合 作为CMS的后备预案,在并发收集发生Concurrent Mode Failure时使用 | 标记-整理 | Client模式
|
Parallel Old(并行GC) | -XX:+UseParallelOldGC( Parallel Scavenge + Parallel Old) | 老年代 | 多线程 吞吐量优先 | 标记-整理 | 吞吐量优先,CPU资源敏感 |
CMS(并发GC) | -XX:+UseConcMarkSweepGC | 老年代 | 目的:获取最短回收停顿时间
CMS收集器的内存回收过程是与用户线程一起并发执行的。
CMS收集器的优点:
并发收集、低停顿
三个缺点:
1)CMS收集器对CPU资源非常敏感。
2)CMS收集器无法处理浮动垃圾,可能出现“Concurrent Mode Failure“,失败后而导致另一次Full GC的产生。由于CMS并发清理阶段用户线程还在运行,伴随程序的运行自热会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在本次收集中处理它们,只好留待下一次GC时将其清理掉。这一部分垃圾称为“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,
即需要预留足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分内存空间提供并发收集时的程序运作使用。在默认设置下,CMS收集器在老年代使用了68%的空间时就会被激活,也可以通过参数-XX:CMSInitiatingOccupancyFraction的值来提供触发百分比,以降低内存回收次数提高性能。要是CMS运行期间预留的内存无法满足程序其他线程需要,就会出现“Concurrent Mode Failure”失败,这时候虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。所以说参数-XX:CMSInitiatingOccupancyFraction设置的过高将会很容易导致“Concurrent Mode Failure”失败,性能反而降低。
3)CMS是基于“标记-清除”算法实现的收集器,使用“标记-清除”算法收集后,会产生大量碎片。空间碎片太多时,将会给对象分配带来很多麻烦,比如说大对象,内存空间找不到连续的空间来分配不得不提前触发一次Full GC。为了解决这个问题,CMS收集器提供了一个-XX:UseCMSCompactAtFullCollection开关参数,用于在Full GC之后增加一个碎片整理过程,还可通过-XX:CMSFullGCBeforeCompaction参数设置执行多少次不压缩的Full GC之后,跟着来一次碎片整理过程。
收集过程分为: 1)初始标记 (stop the world),仅仅只是标记出GC ROOTS能直接关联到的对象,速度很快 2)并发标记,进行GC ROOTS 根搜索算法阶段,会判定对象是否存活 3)重新标记 (stop the world) 4)并发收集 | 标记-清除 | 互联网站或者B/S系统,交互体验好 |
G1 | -XX:+UseG1GC | G1收集器基于“标记-整理”算法实现,也就是说不会产生内存碎片 G1垃圾收集的范围是整个Java堆(包括新生代,老年代) 化整为零,G1把整个堆划分成若干Region,后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region(Gargage-First的由来)
收集过程分为: 1)初始标记 (stop the world) 2)并发标记 3)最终标记 4)筛选回收 | 标记-整理 | 面向服务端应用,目的是替代CMS |
JVM启动参数共分为三类:
1) 旧生代空间不足
旧生代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:
java.lang.OutOfMemoryError: Java heap space
为避免以上两种状况引起的Full GC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。
2) Perman Generation空间满
Perman Generation中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Perman Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息:
java.lang.OutOfMemoryError: PermGen space
为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。
3) CMS GC时出现promotion failed和concurrent mode failure
对于采用CMS进行旧生代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。
promotion failed是在进行Minor GC时,survivor space放不下、对象只能放入旧生代,而此时旧生代也放不下造成的;
concurrent mode failure是在执行CMS GC的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。
应对措施为:增大survivor space、旧生代空间或调低触发并发GC的比率,但在JDK 5.0+、6.0+的版本中有可能会由于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。
对于这种状况,可通过设置-XX: CMSMaxAbortablePrecleanTime=5(单位为ms)来避免。
4) 统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间
这是一个较为复杂的触发情况,Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。
例如程序第一次触发Minor GC后,有6MB的对象晋升到旧生代,那么当下一次Minor GC发生时,首先检查旧生代的剩余空间是否大于6MB,如果小于6MB,则执行Full GC。
当新生代采用PS GC时,方式稍有不同,PS GC是在Minor GC后也会检查,例如上面的例子中第一次Minor GC后,PS GC会检查此时旧生代的剩余空间是否大于6MB,如小于,则触发对旧生代的回收。
除了以上4种状况外,对于使用RMI来进行RPC或管理的Sun JDK应用而言,默认情况下会一小时执行一次Full GC。
可通过在启动时通过- java -Dsun.rmi.dgc.client.gcInterval=3600000来设置Full GC执行的间隔时间
通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc