JVM 之 GC日志分析

http://blog.csdn.net/lan861698789/article/details/51985188

[+]



常用JVM参数

http://blog.csdn.NET/gzh0222/article/details/8223277 

分析gc日志后,经常需要调整jvm内存相关参数,常用参数如下

-Xms初始堆大小,默认为物理内存的1/64(<1GB);默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制

-Xmx:最大堆大小,默(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM减少堆直到 -Xms的最小限制

-Xmn新生代的内存空间大小,注意:此处的大小是(eden+ 2 survivor space)。与jmap -heap中显示的New gen是不同的。整个堆大小=新生代大小 + 老生代大小 + 永久代大小。 
在保证堆大小不变的情况下,增大新生代后,将会减小老生代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8

-XX:SurvivorRatio新生代中Eden区域与Survivor区域的容量比值,默认值为8。两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10

-Xss每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。应根据应用的线程所需内存大小进行适当调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。一般小的应用, 如果栈不是很深, 应该是128k够用的,大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。和threadstacksize选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:"-Xss is translated in a VM flag named ThreadStackSize”一般设置这个值就可以了。

-XX:PermSize设置永久代(perm gen)初始值。默认值为物理内存的1/64

-XX:MaxPermSize设置持久代最大值。物理内存的1/4


-----------------------------------------------------------------------------------------------------------------------

GC参数:

http://jackyrong.iteye.com/blog/2245223

1. 在eclipse根目录下的eclipse.ini配置文件中添加以下参数: 
-verbose:gc (开启打印垃圾回收日志) 
-Xloggc:eclipse_gc.log (设置垃圾回收日志打印的文件,文件名称可以自定义) 
-XX:+PrintGCTimeStamps (打印垃圾回收时间信息时的时间格式) 
-XX:+PrintGCDetails (打印垃圾回收详情) 
添加完以上参数后当启动Eclipse后就能在Eclipse根目录看到一个eclipse_gc.log的gc日志文件 

[html]  view plain  copy
  1. -Xmx11M -Xms4M -verbose:gc -Xloggc:eclipse_gc.log -XX:+PrintGCTimeStamps -XX:+PrintGCDetails  


tomcat:

[html]  view plain  copy
  1. SET CATALINA_OPTS=-XX:ParallelGCThreads=4 -XX:+PrintGCDetails -Xloggc:D:/myApplication/tomcat/apache-tomcat-6.0.20/logs/tomcat_gc.log  


2、设置eclipse初始堆、非堆内存大小以及年轻代 
-Xms50m –Xmx200m -XX:PermSize=30m -XX:MaxPermSize=60m 
3、添加JVM监控参数 
-Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.port=6688 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false 

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

名词解释:

full gc

full gc是对新生代,旧生代,以及持久代的统一回收,由于是对整个空间的回收,因此比较慢,系统中应当尽量减少full gc的次数。

如下几种情况下会发生full gc:

  1. 旧生代空间不足
  2. 持久代空间不足
  3. CMS GC时出现了promotion failed和concurrent mode failure
  4. 统计得到新生代minor gc时晋升到旧生代的平均大小小于旧生代剩余空间
  5. 直接调用System.gc,可以DisableExplicitGC来禁止
  6. 存在rmi调用时,默认会每分钟执行一次System.gc,可以通过-Dsun.rmi.dgc.server.gcInterval=3600000来设置大点的间隔。


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

GC日志分析

http://blog.csdn.net/huangzhaoyang2009/article/details/11860757

JVM的GC日志的主要参数包括如下几个:

-XX:+PrintGC 输出GC日志

-XX:+PrintGCDetails 输出GC的详细日志

-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)

-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)

-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息

-Xloggc:../logs/gc.log 日志文件的输出路径

在我做了如下的设置

Java代码   收藏代码
  1. -XX:+PrintGCDetails -Xloggc:../logs/gc.log -XX:+PrintGCTimeStamps  

 

以后打印出来的日志为:

Java代码   收藏代码
  1. 0.756: [Full GC (System) 0.756: [CMS: 0K->1696K(204800K), 0.0347096 secs] 11488K->1696K(252608K), [CMS Perm : 10328K->10320K(131072K)], 0.0347949 secs] [Times: user=0.06 sys=0.00, real=0.05 secs]  
  2. 1.728: [GC 1.728: [ParNew: 38272K->2323K(47808K), 0.0092276 secs] 39968K->4019K(252608K), 0.0093169 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]  
  3. 2.642: [GC 2.643: [ParNew: 40595K->3685K(47808K), 0.0075343 secs] 42291K->5381K(252608K), 0.0075972 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]  
  4. 4.349: [GC 4.349: [ParNew: 41957K->5024K(47808K), 0.0106558 secs] 43653K->6720K(252608K), 0.0107390 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]  
  5. 5.617: [GC 5.617: [ParNew: 43296K->7006K(47808K), 0.0136826 secs] 44992K->8702K(252608K), 0.0137904 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]  
  6. 7.429: [GC 7.429: [ParNew: 45278K->6723K(47808K), 0.0251993 secs] 46974K->10551K(252608K), 0.0252421 secs]  

 

我们取倒数第二条记录分析一下各个字段都代表了什么含义

Java代码   收藏代码
  1. 5.617(时间戳): [GC(Young GC) 5.617(时间戳): [ParNew(使用ParNew作为年轻代的垃圾回收期): 43296K(年轻代垃圾回收前的大小)->7006K(年轻代垃圾回收以后的大小)(47808K)(年轻代的总大小), 0.0136826 secs(回收时间)] 44992K(堆区垃圾回收前的大小)->8702K(堆区垃圾回收后的大小)(252608K)(堆区总大小), 0.0137904 secs(回收时间)] [Times: user=0.03(Young GC用户耗时) sys=0.00(Young GC系统耗时), real=0.02 secs(Young GC实际耗时)]  

 

我们再对数据做一个简单的分析

从最后一条GC记录中我们可以看到 Young GC回收了 45278-6723=38555K的内存

Heap区通过这次回收总共减少了 46974-10551=36423K的内存。

38555-36423=2132K说明通过该次Young GC有2132K的内存被移动到了Old Gen,

 

我们来验证一下

在最后一次Young GC的回收以前 Old Gen的大小为8702-7006=1696

回收以后Old Gen的内存使用为10551-6723=3828

Old Gen在该次Young GC以后内存增加了3828-1696=2132K 与预计的相符

 

重新设置GC日志的输出

 

Java代码   收藏代码
  1. -XX:+PrintGCDetails  
  2. -XX:+PrintHeapAtGC  
  3. -XX:+PrintGCDateStamps  
  4. -XX:+PrintTenuringDistribution  
  5. -verbose:gc  
  6. -Xloggc:gc.log  

后可以看到进行GC前后的堆内存信息 

Java代码   收藏代码
  1. {Heap before GC invocations=1 (full 0):  
  2.  PSYoungGen      total 152896K, used 131072K [0x00000000f55600000x00000001000000000x0000000100000000)  
  3.   eden space 131072K, 100% used [0x00000000f5560000,0x00000000fd560000,0x00000000fd560000)  
  4.   from space 21824K, 0% used [0x00000000feab0000,0x00000000feab0000,0x0000000100000000)  
  5.   to   space 21824K, 0% used [0x00000000fd560000,0x00000000fd560000,0x00000000feab0000)  
  6.  PSOldGen        total 349568K, used 0K [0x00000000e00000000x00000000f55600000x00000000f5560000)  
  7.   object space 349568K, 0% used [0x00000000e0000000,0x00000000e0000000,0x00000000f5560000)  
  8.  PSPermGen       total 26432K, used 26393K [0x00000000d00000000x00000000d19d00000x00000000e0000000)  
  9.   object space 26432K, 99% used [0x00000000d0000000,0x00000000d19c64a0,0x00000000d19d0000)  
  10. 2013-05-05T23:16:10.480+08005.228: [GC  
  11. Desired survivor size 22347776 bytes, new threshold 7 (max 15)  
  12.  [PSYoungGen: 131072K->8319K(152896K)] 131072K->8319K(502464K), 0.0176346 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]   
  13. Heap after GC invocations=1 (full 0):  
  14.  PSYoungGen      total 152896K, used 8319K [0x00000000f55600000x00000001000000000x0000000100000000)  
  15.   eden space 131072K, 0% used [0x00000000f5560000,0x00000000f5560000,0x00000000fd560000)  
  16.   from space 21824K, 38% used [0x00000000fd560000,0x00000000fdd7ff78,0x00000000feab0000)  
  17.   to   space 21824K, 0% used [0x00000000feab0000,0x00000000feab0000,0x0000000100000000)  
  18.  PSOldGen        total 349568K, used 0K [0x00000000e00000000x00000000f55600000x00000000f5560000)  
  19.   object space 349568K, 0% used [0x00000000e0000000,0x00000000e0000000,0x00000000f5560000)  
  20.  PSPermGen       total 26432K, used 26393K [0x00000000d00000000x00000000d19d00000x00000000e0000000)  
  21.   object space 26432K, 99% used [0x00000000d0000000,0x00000000d19c64a0,0x00000000d19d0000)  
  22. }  

 

Java代码   收藏代码
  1. [0x00000000f5560000,0x00000000f5560000,0x00000000fd560000)  

这种形式的日志有两种意义: 
当这种日志出现在generation的详细信息里的时候,三个数字在HotSpot里分别称为low_boundary、high、high_boundary。 
low_boundary: reserved space的最低地址边界;通常也跟“low”相等,这是commited space的最低地址边界 
high: commited space的最高地址边界 
high_boundary: reserved space的最高地址边界。 

[low_boundary, high_boundary)范围内的就是reserved space,这个space的大小就是max capacity。 
[low, high)范围内的就是commited space,而这个space的大小就是current capacity(当前容量),简称capacity。 
capacity有可能在一对最小值和最大值之间浮动。最大值就是上面说的max capacity。


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

详解Java GC的工作原理

JVM内存管理和JVM垃圾回收

JVM内存组成结构

JVM内存结构由堆、栈、本地方法栈、方法区等部分组成,结构图如下所示:


1)堆

所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制。堆被划分为新生代和旧生代,新生代又被进一步划分为Eden和Survivor区,最后Survivor由FromSpace和ToSpace组成,结构图如下所示:


新生代。新建的对象都是用新生代分配内存,Eden空间不足的时候,会把存活的对象转移到Survivor中,新生代大小可以由-Xmn来控制,也可以用-XX:SurvivorRatio来控制Eden和Survivor的比例。旧生代用于存放新生代中经过多次垃圾回收(也即Minor GC)仍然存活的对象

2)栈

每个线程执行每个方法的时候都会在栈中申请一个栈帧,每个栈帧包括局部变量区和操作数栈,用于存放此次方法调用过程中的临时变量、参数和中间结果

3)本地方法栈

用于支持native方法的执行,存储了每个native方法调用的状态

4)方法区

存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。JVM用持久代(PermanetGeneration)来存放方法区,可通过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值。介绍完了JVM内存组成结构,下面我们再来看一下JVM垃圾回收机制。

JVM垃圾回收机制

JVM分别对新生代和旧生代采用不同的垃圾回收机制

新生代的GC:

新生代通常存活时间较短,因此基于Copying算法来进行回收,所谓Copying算法就是扫描出存活的对象,并复制到一块新的完全未使用的空间中,对应于新生代,就是在Eden和FromSpace或ToSpace之间copy。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从eden到survivor,最后到旧生代,

用javavisualVM来查看,能明显观察到新生代满了后,会把对象转移到旧生代,然后清空继续装载,当旧生代也满了后,就会报outofmemory的异常,如下图所示:


在执行机制上JVM提供了串行GC(SerialGC)、并行回收GC(ParallelScavenge)和并行GC(ParNew)

1)串行GC

在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求不是非常高的应用上,是client级别默认的GC方式,可以通过-XX:+UseSerialGC来强制指定

2)并行回收GC

在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数

3)并行GC

与旧生代的并发GC配合使用

旧生代的GC:

旧生代与新生代不同,对象存活的时间比较长,比较稳定,因此采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,回收后对用空出的空间要么进行合并,要么标记出来便于下次进行分配,总之就是要减少内存碎片带来的效率损耗。在执行机制上JVM提供了串行GC(SerialMSC)、并行GC(parallelMSC)和并发GC(CMS),具体算法细节还有待进一步深入研究。

以上各种GC机制是需要组合使用的,指定方式由下表所示:

GC机制组合使用



-----------------------------------------------------------------------------------------------------------------------


排查记录

http://caogen81.iteye.com/blog/1513345

相关文章
相关标签/搜索