三种GC大揭秘【转】

 转载自    http://longdick.javaeye.com/blog/474764

 

 

(本文基于JDK6)

 

说到GC,首先要对Java 的内存模型有所了解。

Java 的内存模型各个代的默认排列有如下图(适用JDK1.4.*  到 JDK6):


Java 的内存模型分为

Young(年轻代)

Tenured(终身代)

Perm(永久代)

 

更多关于内存模型的文章看这里:

图解JVM在内存中申请对象及垃圾回收流程

图解JVM内存模型

 

在堆内存中的GC可以分为Minor GC(次要GC)和 Major GC(主要GC),次要GC是在年轻代进行收集的GC,职责是在Eden区满的时候收集dead的对象和转移存活的对象;主要GC是在终生代满时进行收集的GC,主要GC较次要GC需要更多时间。
使用jvm参数-verbose:gc 就可以输出每一次GC的详细信息。

你可能在程序运行起来以后看到如下输出:

 

[GC 325407K->83000K(776768K), 0.2300771 secs]
[GC 325816K->83372K(776768K), 0.2454258 secs]
[Full GC 267628K->83769K(776768K), 1.8479984 secs]





你可以看到两次Minor GC(次要GC)和一次Major GC(主要GC)。

325407K->83000K   箭头前后的数字分别代表收集前和收集后的堆内存占用情况。
(776768K) 括号内的数字代表总共分配的堆内存空间,注意,这个值不包括其中一个Survior空间,也不包括 permanent generation(永久代)。

最后的时间0.2300771 secs指的是GC所耗费的时间。

 

如果运行时加上VM参数-XX:+PrintGCDetails    将输出更详细的信息。如下显示了Eden区和Heap内存在GC前后的变化:

 

[GC [DefNew: 64575K->959K(64576K), 0.0457646 secs] 196016K->133633K(261184K), 0.0459067 secs]




 

如果运行时加上VM参数-XX:+PrintGCTimeStamps 则可以得到GC发生的时间。

以下输出显示了在程序运行到111.042 秒的时候发生的GC,包括一次在Eden区的次要GC和发生在Tenured区的主要GC:

 

111.042: [GC 111.042: [DefNew: 8128K->8128K(8128K), 0.0000505 secs]111.042: [Tenured: 18154K->2311K(24576K), 0.1290354 secs] 26282K->2311K(32704K), 0.1293306 secs]

 

GC性能主要的衡量指标有两个:Throughput和Pauses。吞吐量(Throughput)是不做GC的时间与总时间的百分比,分子包括分配内存空间的时间。中断(Pauses)是测量时间段内由于GC而导致的应用暂停次数。

对用户而言,对GC的需求往往是不一样的。一般的web应用对吞吐量要求不高,由于GC而引起的偶尔中断也是可以容忍的。然而一个交互性强的实时应用系统来说,经常性的中断将带来糟糕的用户体验。

即时性(Promptness)和足印(footprint)也是某些用户考虑的问题。 即时性是对象死去到所占内存释放的时间间隔,这个指数是分布式应用如使用RMI的分布式应用的一项需要考虑的因素。足印是一种过程的集合,代表可伸缩性。

 

 

HOTSPOT JVM总共拥有3种不同的GC,各有各总自的特点和应用场景:

 

  • serial collector (串行GC)任何时刻都是使用一个线程执行GC操作,这种GC在线程间通信没有大的开销的应用会有相对不错的运行效率。最适合单处理器的系统;多处理器系统对这种GC而言并不能提升收集的效率。JVM默认情况下就是使用这个GC,这种GC有个形象的别名叫做"stop-the-world",当JVM在用这个GC收集垃圾的时候,你的app别想干其他事。你也可以用这个参数 -XX:+UseSerialGC 显式的声明使用。 默认的serial gc可以应付绝大多数的app。除非以下情况:这是一个运行在大内存多处理器的机器上的多线程的大应用。
  • parallel collector (并行GC,或者叫 throughput collector ) 会以并行的方式运行minor collections(次级GC), 能较大的减少GC的开销。其诞生的初衷就是专门给运行在多处理器,多线程硬件上的中大型应用的。在特定的硬件和OS环境条件下这是默认选项,显式声明使用 -XX:+UseParallelGC 参数。始于JDK1.3.1。 parallel compaction 是在 J2SE 5.0 update 6引入的新特性,并在Java SE 6 得到增强。它允许使用并行的方式运行Major collections(主要GC)。如果不开启parallel compaction, major collections 将以单一线程的方式运行。 通过参数 -XX:+UseParallelOldGC 显式使用该特性。
  • concurrent collector (同步GC)同时运行大多数的任务 (GC的同时应用也在运行)来保证GC引起的中断时间尽量的短。主要应用在实时性要求重于总体吞吐量要求的中大型应用,即使如此,降低中断时间的技术还是会导致应用程序性能的少许降低。可以使用参数 -XX:+UseConcMarkSweepGC 使用该特性。

 

parallel collector的一些注意点:

parallel collector从JDK5开始就是Server端JVM的默认选择,需要加VM参数 -server

parallel collector还采用一些细节调优的策略如:

  • 限定最大GC中断时间
  • 吞吐量限定
  • 足印(伸缩量)设定

可以用vm参数 -XX:MaxGCPauseMillis=<N> 来限定最大GC中断时间,单位是ms,规定GC产生的中断时间不能超过指定的时间。默认没有这个限制。一旦使用了这个参数,heap空间和其他相关参数会做出相应的调整来满足最大GC中断时间的要求。

吞吐量限定使用-XX:GCTimeRatio=<N> 来设定GC时间的比率,N 的值 = 没有花在GC上的时间/GC的时间 因此GC的时间占用总时间的百分比公式= 1 / (1 + <N>) 。比如 -XX:GCTimeRatio=19 意味着将有1/20的时间花在GC上。默认值=99。

 

足印(伸缩量)实际上就是heap堆内存的调整。最大Heap容量使用参数 -Xmx<N> 声明。

 

以上参数中任何一个的改动,都会引起另外两个的改变。三者的优先级如上顺序一至。

如果太多时间黑白花费在GC上,parallel collector将抛出OOM,这临界值大概是98%;也可以使用-XX:-UseGCOverheadLimit 关闭这个特性。

 

concurrent collector的一些注意点:

 

不适用于单处理器的系统,事实上在单处理器系统上运行concurrent collector 效率反而降低。如果只能运行在单处理器的系统上,那记得开启增量模式(incremental mode)。

前面几种GC都是在Tenured区满了以后触发主要GC操作;concurrent collector却是在Tenured区满溢之前就进行主要GC。如果concurrent collector没有赶在Tenured区满前收集完或者还没有开始收集的话,就会产生长时间的中断。参数-XX:CMSInitiatingOccupancyFraction=<N> 可以指定触发主要GC的临界值,N(0-100)代表的是Tenured区饱和程度百分比。一旦Tenured区饱和程度达到这个临界值,主要GC就发生了。

 

concurrent collection的生命周期一般包括如下几步:

  • 停止应用的所有线程,标识出所有可到达的对象集合,然后恢复应用的所有线程
  • 使用一个或几个处理器资源同步跟踪可到达的对象,应用线程同时运行
  • 使用一个处理器资源同步地重新定位那些自从上一个步骤以来可能修改过的对象
  • 停止应用的所有线程,重新定位那些自从上一个步骤以来可能修改过的对象,然后恢复应用的所有线程
  • 使用一个处理器资源同步地收集不被引用的对象
  • 使用一个处理器资源同步地重新定义堆内存,并且为下一次GC生命周期作好数据准备

concurrent collection在整个收集的过程中,至少会占有一到两个处理器,而且不会自动放弃占有的处理器资源。

这个特性会让只有一到两个处理器的系统很难过。为处理这个问题,需要借助增量模式(incremental mode)。

 

增量模式 的核心思想是 将整个GC生命周期分解成一段段的时间块分步进行,以此减少中断的时间,但是不可避免的是伴随着吞吐量的下降。

使用参数 -XX:+CMSIncrementalMode 开启增量模式。

 

 

参考资料:

http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html

http://java.sun.com/j2se/1.5.0/docs/guide/vm/gc-ergonomics.html

为什么要学JVM1、一切JAVA代码都运行在JVM之上,只有深入理解虚拟机才能写出更强大的代码,解决更深层次的问题。2、JVM是迈向高级工程师、架构师的必备技能,也是高薪、高职位的不二选择。3、同时,JVM又是各大软件公司笔试、面试的重中之重,据统计,头部的30家互利网公司,均将JVM作为笔试面试的内容之一。4、JVM内容庞大、并且复杂难学,通过视频学习是最快速的学习手段。课程介绍本课程包含11个大章节,总计102课时,无论是笔试、面试,还是日常工作,可以让您游刃有余。第1章 基础入门,从JVM是什么开始讲起,理解JDK、JRE、JVM的关系,java的编译流程和执行流程,让您轻松入门。第2章 字节码文件,深入剖析字节码文件的全部组成结构,以及javap和jbe可视化反解析工具的使用。第3章 类的加载、解释、编译,本章节带你深入理解类加载器的分类、范围、双亲委托策略,自己手写类加载器,理解字节码解释器、即时编译器、混合模式、热点代码检测、分层编译等核心知识。第4章 内存模型,本章节涵盖JVM内存模型的全部内容,程序计数器、虚拟机栈、本地方法栈、方法区、永久代、元空间等全部内容。第5章 对象模型,本章节带你深入理解对象的创建过程、内存分配的方法、让你不再稀里糊涂。第6章 GC基础,本章节是垃圾回收的入门章节,带你了解GC回收的标准是什么,什么是可达性分析、安全点、安全区,四种引用类型的使用和区别等等。第7章 GC算法与收集器,本章节是垃圾回收的重点,掌握各种垃圾回收算法,分代收集策略,7种垃圾回收器的原理和使用,垃圾回收器的组合及分代收集等。第8章 GC日志详解,各种垃圾回收器的日志都是不同的,怎么样读懂各种垃圾回收日志就是本章节的内容。第9章 性能监控与故障排除,本章节实战学习jcmd、jmx、jconsul、jvisualvm、JMC、jps、jstatd、jmap、jstack、jinfo、jprofile、jhat总计12种性能监控和故障排查工具的使用。第10章 阿里巴巴Arthas在线诊断工具,这是一个特别小惊喜,教您怎样使用当前最火热的arthas调优工具,在线诊断各种JVM问题。第11章 故障排除,本章会使用实际案例讲解单点故障、高并发和垃圾回收导致的CPU过高的问题,怎样排查和解决它们。课程资料课程附带配套项目源码2个159页高清PDF理论篇课件1份89页高清PDF实战篇课件1份Unsafe源码PDF课件1份class_stats字段说明PDF文件1份jcmd Thread.print解析说明文件1份JProfiler内存工具说明文件1份字节码可视化解析工具1份GC日志可视化工具1份命令行工具cmder 1份学习方法理论篇部分推荐每天学习2课时,可以在公交地铁上用手机进行学习。实战篇部分推荐对照视频,使用配套源码,一边练习一遍学习。课程内容较多,不要一次性学太多,而是要循序渐进,坚持学习。      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值