(本文基于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.0000505secs]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
转载自:http://longdick.iteye.com/blog/474764
分享到:
相关推荐
GC java 手册 kindle格式 GC java 手册 kindle格式 GC java 手册 kindle格式
让你对java gc 的工作原理有更深的了解 谢谢下载
Java垃圾回收GC机制
java7 GC 参数配置说明文档,详细介绍了每个参数的作用
Java垃圾回收GC.xmind
Java基础[Java基础]--Java GC工作原理
poi读取大量数据会造成gc内存溢出的报错,由于垃圾回收机制无法将大量的对象及时的回收,而这些对象又会保存在内存中,会导致内存不够用的情况,这时候我们就需要使用新的方法,读取为cvs即可.此解决方案可支持千万数据的...
NULL 博文链接:https://wangwengcn.iteye.com/blog/1606192
GChisto及CMS GC相应补丁文件,补丁文件未亲测。 This patch adds the following features and improvements when using CMS GC in incremental mode: detecting Full GCs corrected parsing errors when using -XX:...
GC有两种类型:Scavenge GC(也称Young GC)和Full GC。 一般Full GC时,机器的Load会升高,应用也会停止响应一会(持续长达几秒),如果应用一直频繁的进行FullGC,一方面会出现应用无法提供正常服务,另一方面...
Java VisualVM GC插件
GCViewer 能否分析 java 程序 GC 日志,能否图表展示堆内存,年轻代,老年代,永久带以及full gc 的使用情况
java虚拟机中gc的基本原理 .docx
java查看哪个进程频繁GC垃圾回收
内存管理简介 内存管理的职责为分配内存,回收内存。 没有自动内存管理的语言/平台容易发生错误。 典型的问题包括悬挂指针问题,一个指针引用了一个已经被回收的内存地址,导致程序的运行完全不可知。...
Java GC与性能调优文档 作者:高飞
NULL 博文链接:https://xdjava.iteye.com/blog/2031254
一个优秀的Java程序员必须了解GC的工作原理、如何优化GC的性能、如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统、实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的...
java垃圾回收(gc)机制详解
JVM内存管理的介绍,编写GC友好的代码。 本材料主要关心 Sun Hotspot JVM 6的内存管理 Sun Hotspot JVM 6的GC模型 主要针对JVM6的GC模型,但也会简单介绍Java 7的G1 编写GC友好代码的一些技巧