【2015工作总结】JVM监控总结
一。讲解JVM结构
其中,
新生代是Heap,包括Eden(伊甸园)+S0(幸存0)+S1(幸存1):新建对象都存储在这里。配置参数是Xms。
老生代是Old,存放从新生代迁移过来的生命周期较久的对象。新生代和老生代共同组成了堆内存。配置参数是Xmx减去Xmn。
永久代是Perm,是非堆内存的组成部分。存放加载的Class类级对象如class本身,method,field等。
二。一般程序内存溢出分析:
如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。原因有二:
(1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。
(2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。
如果出现java.lang.OutOfMemoryError: PermGen space,说明是Java虚拟机对永久代Perm内存设置不够。
一般出现这种情况,都是程序启动需要加载大量的第三方jar包。例如:在一个Tomcat下部署了太多的应用。
三。具体JVM监控方法:
使用JDK1.7自带的jvisualvm.exe工具,文件路径:..\Java\jdk1.7.0_67\bin\jvisualvm.exe
需要操作以下几个步骤:
3.1修改服务器tomcat的catalina.sh,路径是.../tomcat/bin/catalina.sh(黄色是添加的)
JAVA_OPTS="$JAVA_OPTS -server -Xms${xms_size} -Xmx${xmx_size} -XX:PermSize=512m -XX:MaxNewSize=1024m -XX:MaxPermSize=512m -Djava.awt.headless=true -Dfile.encoding=utf-8 -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=19999 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
3.2添加服务器的应用列表
先创建一个java.policy文件:
grant codebase "file:${java.home}/../lib/tools.jar" { permission java.security.AllPermission; };
放在服务器某个位置,比如我放在JDK1.7的bin路径下,进入对应路径,执行以下命令:
jstatd -J-Djava.security.policy=java.policy -J-Djava.rmi.server.logCalls=true
3.3然后在本地打开jvisualvm.exe,安装完GC插件,就可以看到某个端口的应用JVM监控了。
四。简单介绍JVM的其他知识:
4.1 高并发下 JVM占用TOP100 (假设进程号19771):jmap -histo:live 19771 | head -n 100
开发可以从中分析哪些代码存在不断实例,不断增大,且没有释放的问题。
4.2 内存申请过程
4.2.1 JVM会试图为相关Java对象在Eden中初始化一块内存区域;
4.2.2当Eden空间足够时,内存申请结束。否则到下一步;
4.2.3JVM试图释放在Eden中所有不活跃的对象(minor collection),释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区;
4.2.4.Survivor区被用来作为Eden及old的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区;
4.2.5当old区空间不够时,JVM会在old区进行major collection;
4.2.6完全垃圾收集后,若Survivor及old区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现"Out of memory错误"。
4.3 GC
JVM有2个GC线程:
第一个线程负责回收Heap的新生代区
第二个线程在Heap不足时,遍历Heap,将新生代区升级为老生代区
堆内存GC:JVM(采用分代回收的策略),用较高的频率对年轻的对象(young generation)进行YGC,而对老对象(tenured generation)较少(tenured generation 满了后才进行)进行Full GC。这样就不需要每次GC都将内存中所有对象都检查一遍。
非堆内存不GC:GC不会在主程序运行期对PermGen Space进行清理,所以如果你的应用中有很多CLASS(特别是动态生成类,当然permgen space存放的内容不仅限于类)的话,就很可能出现PermGen Space错误。
监视JVM GC:可以用JDK中的jstat工具,也可以在java程序启动的opt里加上如下几个参数(注:这两个参数只针对SUN的HotSpotVM)
[java] view plaincopy -XX:-PrintGCPrintmessagesatgarbagecollection.Manageable. -XX:-PrintGCDetailsPrintmoredetailsatgarbagecollection.Manageable.(Introducedin1.4.0.) -XX:-PrintGCTimeStampsPrinttimestampsatgarbagecollection.Manageable(Introducedin1.4.0.) [java] view plain copy -XX:-PrintGCPrintmessagesatgarbagecollection.Manageable. -XX:-PrintGCDetailsPrintmoredetailsatgarbagecollection.Manageable.(Introducedin1.4.0.) -XX:-PrintGCTimeStampsPrinttimestampsatgarbagecollection.Manageable(Introducedin1.4.0.)
当把-XX:-PrintGCDetails加入到javaopt里以后可以看见如下输出:
[GC[DefNew:34538K->2311K(36352K),0.0232439secs]45898K->15874K(520320K),0.0233874secs]
[FullGC[Tenured:13563K->15402K(483968K),0.2368177secs]21163K->15402K(520320K),[Perm:28671K->28635K(28672K)],0.2371537secs]
他们分别显示了JVM GC的过程,清理出了多少空间。第一行GC使用的是‘普通GC’(MinorCollections),第二行使用的是‘全GC’(MajorCollections)。他们的区别很大,在第一行最后我们可以看见他的时间是0.0233874秒,而第二行的FullGC的时间是0.2371537秒。第二行的时间是第一行的接近10倍,也就是我们这次调优的重点,减少FullGC的次数,以为FullGC会暂停程序比较长的时间,如果FullGC的次数比较多。程序就会经常性的假死。