【JVM命令及问题排查】

java中的gc log解读

eclipse的优化 gc.log

一次让人难以忘怀的排查频繁Full GC过程

一个GC频繁的Case

堆内存占用很小 但是 JVM 频繁full gc 问题排查

JVM中GC时,堆内存是如何动态变化的(JDK1.7)

使用jmap和MAT分析JVM堆内存

Jmap+MAT 排查内存泄漏

关于内存溢出

Young(Nursery):年轻代

Old(Tenured):年老代

Permanent(Perm):持久代

        装载Class信息等基础数据

堆设置 

-Xms:初始堆大小 ,默认是物理内存的1/64

-Xmx:最大堆大小 

-Xmn:年轻代大小

-Xss:jvm启动的每个线程分配的内存大小

-XX:NewSize=n:设置年轻代大小 

-XX:PermSize=64m设置持久代大小

-XX:MaxPermSize=n:设置持久代最大大小

          MaxPermSize过小会导致:java.lang.OutOfMemoryError: PermGen space

-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4 

-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5 

-XX:MaxTenuringThreshold 

          如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。

-Xmx512m: (max)堆最大可用内存为512M。

-Xms512m: (small)堆初始内存为512m。

此值可以设置与-Xmx最大值相同,以避免每次垃圾回收完成后JVM动态调节Java堆大小而耗费延长其周期。 

-Xmn192m :(new)设置年轻代大小为192m。

整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

垃圾回收统计信息 

-XX:+PrintGC 

-XX:+PrintGCDetails 

-XX:+PrintGCTimeStamps 

-Xloggc:filename

-XX:+PrintGC 

输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs] 

[Full GC 121376K->10414K(130112K), 0.0650971 secs]

-XX:+PrintGCDetails 

输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] 

[GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]

-XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与上面两个混合使用 

输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]

默认java程序是不会开启gc log

对于生产环境下的java程序可以加上来生成gc log,方便出现问题的时候去排查

-verbose:gc -Xloggc:/usr/gclog -XX:+PrintGCDetails XX:+PrintGCTimeStamps

收集器设置 

-XX:+UseSerialGC:设置串行收集器 

-XX:+UseParallelGC:设置并行收集器 

-XX:+UseParalledlOldGC:设置并行年老代收集器 

-XX:+UseConcMarkSweepGC:设置并发收集器

-XX:+DisableExplicitGC这个将会忽略手动调用GC的代码使得 System.gc()的调用就会变成一个空调用,完全不会触发任何GC。

======================================================================

java中的gc log解读

https://my.oschina.net/hadooper/blog/418918

eclipse的优化 gc.log

https://blog.csdn.net/z69183787/article/details/39136119

默认java程序是不会开启gc log

对于生产环境下的java程序可以加上来生成gc log,方便出现问题的时候去排查

-verbose:gc -Xloggc:/usr/gclog -XX:+PrintGCDetails XX:+PrintGCTimeStamps

Linux如何开启tomcat的gc日志并查看

进入到了tomcat的bin的目录下中,命令中vi catalina.sh  来编辑

然后在该文件中cygwin=false上面

添加为

JAVA_OPTS="-Xms128m -Xmx256m -Xmn100m -XX:MaxPermSize=30m -Xloggc:/usr/local/tomcat/logs/gc.$$.log"

gc开启完成之后,只要启动了tomcat之后,就可目录下生成了gc的log的日志内容。

场景一:

[GC [DefNew: 3298K->149K(5504K), 0.0053498 secs] 3298K->3221K(9600K), 0.0053750 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

看一下这行:

DefNew表示新生态:3298K->149K(5504K), 0.0053498 secs

表示新生态由3298K回收变成了149K,即之前的3M回收了,【5504K表示该区域的总内存大小】

3298K->3221K(9600K), 0.0053750 secs] 表示整个jvm堆的大小由于没有可以回收的对象,所以总大小本质没发生改变,9600K是java的总堆大小9600K。(虽然分配10M,其中还有持久态等其他内存区域)

[Times: user=0.00 sys=0.00, real=0.01 secs] 表示gc所花费的系统资源

场景二:

0.209: [GC 0.209: [DefNew: 4416K->511K(4928K), 0.0034707 secs] 4416K->614K(15872K), 0.0035239 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]  

6.383: [GC 6.383: [DefNew: 18880K->1985K(21184K), 0.0055311 secs] 46992K->30098K(68040K), 0.0055694 secs]

这6秒中GC日志打了69次, 而内存回收率还是蛮高的 young区18880-1985=16895 jvm 46992-30098=16894 都快接近100%了,可以看出young区是由小到大在不断调整大小,所以不断GC,因此设一个初始值吧,据说设置heap的1/4比较好,那就是 128M,所以eclipse.ini加入 

-Xmn128m

场景三:

103.759: [Full GC (System) 103.759: [Tenured: 81882K->66784K(393216K), 0.3287387 secs] 185350K->66784K(511232K), [Perm : 53464K->53414K(65536K)], 0.3287897 secs] [Times: user=0.33 sys=0.00, real=0.33 secs]

仔细观察日志,发现Full GC (System) 字样,这个意思是eclipse里调用了System.gc()手动触发了系统GC,在eclipse.ini里加入: 

-XX:+DisableExplicitGC

-XX:+DisableExplicitGC这个将会忽略手动调用GC的代码使得 System.gc()的调用就会变成一个空调用,完全不会触发任何GC。

======================================================================

 关于内存溢出

https://blog.csdn.net/bbaiggey/article/details/50885943

第一类内存溢出,也是大家认为最多,第一反应认为是的内存溢出,就是堆栈溢出:

那什么样的情况就是堆栈溢出呢?当你看到下面的关键字的时候它就是堆栈溢出了:

java.lang.OutOfMemoryError: ......java heap space.....

也就是当你看到heap相关的时候就肯定是堆栈溢出了,此时如果代码没有问题的情况下,适当调整-Xmx和-Xms是可以避免的,不过一定是代码没有问题的前提,为什么会溢出呢,要么代码有问题,要么访问量太多并且每个访问的时间太长或者数据太多,导致数据释放不掉,因为垃圾回收器是要找到那些是垃圾才能回收,这里它不会认为这些东西是垃圾,自然不会去回收了;主意这个溢出之前,可能系统会提前先报错关键字为:

java.lang.OutOfMemoryError:GC over head limit exceeded

这种情况是当系统处于高频的GC状态,而且回收的效果依然不佳的情况,就会开始报这个错误,这种情况一般是产生了很多不可以被释放的对象,有可能是引用使用不当导致,或申请大对象导致,但是java heap space的内存溢出有可能提前不会报这个错误,也就是可能内存就直接不够导致,而不是高频GC.

第二类内存溢出,PermGen的溢出,或者PermGen 满了的提示,你会看到这样的关键字:

关键信息为:

java.lang.OutOfMemoryError: PermGen space

原因:系统的代码非常多或引用的第三方包非常多、或代码中使用了大量的常量、或通过intern注入常量、或者通过动态代码加载等方法,导致常量池的膨胀,虽然JDK 1.5以后可以通过设置对永久带进行回收,但是我们希望的是这个地方是不做GC的,它够用就行,所以一般情况下今年少做类似的操作,所以在面对这种情况常用的手段是:增加-XX:PermSize和-XX:MaxPermSize的大小。

第三类内存溢出:在使用ByteBuffer中的allocateDirect()的时候会用到,很多javaNIO的框架中被封装为其他的方法

溢出关键字:

java.lang.OutOfMemoryError: Direct buffer memory

如果你在直接或间接使用了ByteBuffer中的allocateDirect方法的时候,而不做clear的时候就会出现类似的问题,常规的引用程序IO输出存在一个内核态与用户态的转换过程,也就是对应直接内存与非直接内存,如果常规的应用程序你要将一个文件的内容输出到客户端需要通过OS的直接内存转换拷贝到程序的非直接内存(也就是heap中),然后再输出到直接内存由操作系统发送出去,而直接内存就是由OS和应用程序共同管理的,而非直接内存可以直接由应用程序自己控制的内存,jvm垃圾回收不会回收掉直接内存这部分的内存,所以要注意了哦。

如果经常有类似的操作,可以考虑设置参数:-XX:MaxDirectMemorySize

第四类内存溢出错误:

溢出关键字:

java.lang.StackOverflowError

这个参数直接说明一个内容,就是-Xss太小了,我们申请很多局部调用的栈针等内容是存放在用户当前所持有的线程中的,线程在jdk 1.4以前默认是256K,1.5以后是1M,如果报这个错,只能说明-Xss设置得太小,当然有些厂商的JVM不是这个参数,本文仅仅针对Hotspot VM而已;不过在有必要的情况下可以对系统做一些优化,使得-Xss的值是可用的。

相关推荐