(本文基于JDK6)
说到GC,首先要对Java 的内存模型有所了解。
Java 的内存模型各个代的默认排列有如下图(适用JDK1.4.* 到 JDK6):
Java 的内存模型分为
Young(年轻代)
Tenured(终身代)
Perm(永久代)
在堆内存中的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(即SUN的JDK) JVM总共拥有3种不同的GC,各有各总自的特点和应用场景:
-XX:+UseSerialGC 显式的声明使用。
默认的serial gc可以应付绝大多数的app。除非以下情况:这是一个运行在大内存多处理器的机器上的多线程的大应用。-XX:+UseParallelGC
参数。始于JDK1.3.1。 parallel compaction 是在 J2SE 5.0 update 6引入的新特性,并在Java SE 6 得到增强。它允许使用并行的方式运行Major collections(主要GC)。如果不开启parallel compaction, major collections 将以单一线程的方式运行。 通过参数 -XX:+UseParallelOldGC
显式使用该特性。-XX:+UseConcMarkSweepGC 使用该特性。
parallel collector的一些注意点:
parallel collector从JDK5开始就是Server端JVM的默认选择,需要加VM参数 -server 。
parallel collector还采用一些细节调优的策略如:
可以用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的生命周期一般包括如下几步:
concurrent collection在整个收集的过程中,至少会占有一到两个处理器,而且不会自动放弃占有的处理器资源。
这个特性会让只有一到两个处理器的系统很难过。为处理这个问题,需要借助增量模式(incremental mode)。
增量模式 的核心思想是 将整个GC生命周期分解成一段段的时间块分步进行,以此减少中断的时间,但是不可避免的是伴随着吞吐量的下降。
使用参数 -XX:+CMSIncrementalMode 开启增量模式。
转自:http://sishuok.com/forum/blogPost/list/321.html