java虚拟机-jvm内存回收算法篇( 六 )


Serial Old收集器是Serial收集器的老年代版本,它同样是一个单线程收集器,使用‘标记-整理’算法 。这个收集器的主要意义是在于给Client模式下的虚拟机使用 。如果在Server模式下,那么它还有两大用途:一种是在JDK1.5以及之前版本中与Parallel Scavenge收集器搭配使用,另一种是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用 。
3.5.5 Parallel Old收集器
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和标记整理算法 。这个收集器是在JDK1.6才开始提供的,在此之前,新生代的Parallel Scavenge收集器处于比较尴尬的状态,因为新生代选择了Parallel Scavenge收集器,老年代必须选择Serial Old(因为Parallel Scavenge收集器无法与CMS收集器搭配使用) 。Serial Old由于是单线程,在服务端性能是拖累,因此即使使用了Parallel Scavenge收集器也未必能获得吞吐量最大化的效果,而且单线程的老年代收集中无法充分利用服务器多CPU的处理能力,在老年代很大而且硬件比较高级的环境中,这种组合的吞吐量甚至还不一定有ParNew加CMS组合的能力 。
3.5.6 CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器 。主要应用在服务端,以提供较短的响应时间 。它是基于标记-清除算法实现 。整个过程分为4个步骤:
初始标记(CMS initial mark)
并发标记(CMS concurrent mark)
重新标记(CMS remark)
并发清除(CMS concurrent sweep)
其中初始标记和重新标记依然会有停顿时间(Stop the world) 。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记就是进行GC Roots Tracing的过程,而重新标记则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短 。
CMS优点:并发收集,低停顿 。
缺点:
CMS收集器对CPU资源非常敏感 。在CMS中并发阶段,它依赖CPU资源,若CPU资源较少时,CMS会消耗掉比较多的一部分CPU资源,使得程序的执行速度变慢 。
CMS收集器无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生 。由于CMS并发清理阶段用户程序还在运行,就会产生新的垃圾,这一部分垃圾出现在标记过程后,只好留到下一次GC时清理,这一部分垃圾成为
"浮动垃圾" 。
CMS收集器使用的时标记-清除算法,所以会产生内存碎片,导致大对象申请内存时,无法找到足够大的连续空间来分配对象,不得不提前触发Full GC 。
3.5.7 G1收集器
为解决CMS算法产生空间碎片和其它一系列的问题缺陷,HotSpot提供了另外一种垃圾回收策略,G1(Garbage First)算法,通过参数-XX:+UseG1GC来启用,该算法在JDK 7u4版本被正式推出,官网对此描述如下:
G1垃圾收集算法主要应用在多CPU大内存的服务中,在满足高吞吐量的同时,竟可能的满足垃圾回收时的暂停时间,该设计主要针对如下应用场景:
垃圾收集线程和应用线程并发执行,和CMS一样
空闲内存压缩时避免冗长的暂停时间
应用需要更多可预测的GC暂停时间
不希望牺牲太多的吞吐性能
不需要很大的Java堆 (翻译的有点虚,多大才算大?)
第四章 虚拟机性能监控与故障处理工具
4.1 概述
了解了虚拟机内存分配与回收技术的介绍,再根据从实践的角度去了解虚拟机内存管理的世界 。
给一个系统定位问题的时候,知识、经验是关键基础,数据是依据,工具是运用处理数据的手段 。这里说的数据包括:运行日志、异常堆栈、GC日志、线程快照(threaddump/javacore文件)、堆转储快照(heapdump/hprof文件)等 。经常使用适当的虚拟机监控和分析的工具可以加快我们分析数据、定位解决问题的速度,但在学习工具前,也应当意识到工具永远都是知识技能的一层包装,没有什么工具是秘密武器 。
4.2 JDK的命令行工具
Sun jdk监控和故障处理工具

java虚拟机-jvm内存回收算法篇

文章插图
 
4.2.1 jps:虚拟机进程状况工具
java虚拟机-jvm内存回收算法篇

文章插图
 
jps [option] [hostid]
-q 只输出LVMID,省略主类的名称
-m 输出虚拟机进程启动时传递给主类main()函数的参数
-l 输出主类的全名,如果进程执行的是Jar包,输出Jar路径


推荐阅读