一次tomcat自动关闭的bug解决
前言
最近一个运行了4年的javaeeweb项目,经常接到客户反馈系统无法打开。登录服务器查看服务,发现是tomcat自动关闭了。基本是3到4天发生一次。
运维人员开始以为是其他服务杀死了tomcat服务,没放在心上,解决方法就是直接重启tomcat。
最终捅了篓子,运维人员被客户投诉,扣了一个月的绩效。
解决这个bug兜兜转转来到了我这里。既然接到任务,那就开干,没有解决不了的bug。
系统的运行环境如下:
- tomcat6.0
- 32位jdk7.0
- windowserver200332位,32G内存。
查看日志,如果tomcat闪崩,都会在tomcat的bin目录下生成以"hs_err"开头的日志文件。打开最新的日志文件,首先看到的是下面一段话:
#ThereisinsufficientmemoryfortheJavaRuntimeEnvironmenttocontinue. #Nativememoryallocation(malloc)failedtoallocate32756bytesforChunkPool::allocate #Possiblereasons: #ThesystemisoutofphysicalRAMorswapspace #In32bitmode,theprocesssizelimitwashit #Possiblesolutions: #Reducememoryloadonthesystem #Increasephysicalmemoryorswapspace #Checkifswapbackingstoreisfull #Use64bitJavaona64bitOS #DecreaseJavaheapsize(-Xmx/-Xms) #DecreasenumberofJavathreads #DecreaseJavathreadstacksizes(-Xss) #Setlargercodecachewith-XX:ReservedCodeCacheSize= #Thisoutputfilemaybetruncatedorincomplete. # #OutofMemoryError(allocation.cpp:211),pid=7864,tid=6556 # #JREversion:Java(TM)SERuntimeEnvironment(7.0_79-b15)(build1.7.0_79-b15) #JavaVM:JavaHotSpot(TM)ServerVM(24.79-b02mixedmodewindows-x86) #Failedtowritecoredump.
大概意思就是内存不够了,无法分配32756字节的空间。同时给出几个解决方法:
1、减少系统内存负载;
2、增加物理内存或者交换空间;
3、在64位操作系统上使用64位jdk;
4、减少javaheap大小;
5、减少java线程数量;
6、减少java线程堆栈大小。
通过上面的内容可以得出,jvm无法分配32756bytes的内存空间。
从接到任务开始,我一直以为是jvm配置出错,导致内存不够用,只需调整下新生代、老年代的配置即可。
继续往下看日志文件,找到"GCHeapHistory(10events):"这一行,这个记录jvm最后10次垃圾回收时堆的变化情况。
GCHeapHistory(10events): Event:572312.299GCheapbefore {HeapbeforeGCinvocations=5046(full357): PSYoungGentotal201472K,used200685K[0x573c0000,0x63bc0000,0x63bc0000) edenspace198144K,100%used[0x573c0000,0x63540000,0x63540000) fromspace3328K,76%used[0x63540000,0x637bb528,0x63880000) tospace3328K,0%used[0x63880000,0x63880000,0x63bc0000) ParOldGentotal843776K,used422602K[0x23bc0000,0x573c0000,0x573c0000) objectspace843776K,50%used[0x23bc0000,0x3d872b18,0x573c0000) PSPermGentotal262144K,used51848K[0x03bc0000,0x13bc0000,0x23bc0000) objectspace262144K,19%used[0x03bc0000,0x06e62138,0x13bc0000) Event:572312.305GCheapafter HeapafterGCinvocations=5046(full357): PSYoungGentotal201472K,used1103K[0x573c0000,0x63bc0000,0x63bc0000) edenspace198144K,0%used[0x573c0000,0x573c0000,0x63540000) fromspace3328K,33%used[0x63880000,0x63993c90,0x63bc0000) tospace3328K,0%used[0x63540000,0x63540000,0x63880000) ParOldGentotal843776K,used423618K[0x23bc0000,0x573c0000,0x573c0000) objectspace843776K,50%used[0x23bc0000,0x3d970b18,0x573c0000) PSPermGentotal262144K,used51848K[0x03bc0000,0x13bc0000,0x23bc0000) objectspace262144K,19%used[0x03bc0000,0x06e62138,0x13bc0000) } Event:572351.132GCheapbefore {HeapbeforeGCinvocations=5047(full357): PSYoungGentotal201472K,used199247K[0x573c0000,0x63bc0000,0x63bc0000) edenspace198144K,100%used[0x573c0000,0x63540000,0x63540000) fromspace3328K,33%used[0x63880000,0x63993c90,0x63bc0000) tospace3328K,0%used[0x63540000,0x63540000,0x63880000) ParOldGentotal843776K,used423618K[0x23bc0000,0x573c0000,0x573c0000) objectspace843776K,50%used[0x23bc0000,0x3d970b18,0x573c0000) PSPermGentotal262144K,used51848K[0x03bc0000,0x13bc0000,0x23bc0000) objectspace262144K,19%used[0x03bc0000,0x06e62138,0x13bc0000) Event:572351.137GCheapafter HeapafterGCinvocations=5047(full357): PSYoungGentotal201472K,used1615K[0x573c0000,0x63bc0000,0x63bc0000) edenspace198144K,0%used[0x573c0000,0x573c0000,0x63540000) fromspace3328K,48%used[0x63540000,0x636d3ec8,0x63880000) tospace3328K,0%used[0x63880000,0x63880000,0x63bc0000) ParOldGentotal843776K,used423674K[0x23bc0000,0x573c0000,0x573c0000) objectspace843776K,50%used[0x23bc0000,0x3d97eb18,0x573c0000) PSPermGentotal262144K,used51848K[0x03bc0000,0x13bc0000,0x23bc0000) objectspace262144K,19%used[0x03bc0000,0x06e62138,0x13bc0000) } Event:572398.649GCheapbefore {HeapbeforeGCinvocations=5048(full357): PSYoungGentotal201472K,used199759K[0x573c0000,0x63bc0000,0x63bc0000) edenspace198144K,100%used[0x573c0000,0x63540000,0x63540000) fromspace3328K,48%used[0x63540000,0x636d3ec8,0x63880000) tospace3328K,0%used[0x63880000,0x63880000,0x63bc0000) ParOldGentotal843776K,used423674K[0x23bc0000,0x573c0000,0x573c0000) objectspace843776K,50%used[0x23bc0000,0x3d97eb18,0x573c0000) PSPermGentotal262144K,used51848K[0x03bc0000,0x13bc0000,0x23bc0000) objectspace262144K,19%used[0x03bc0000,0x06e62138,0x13bc0000) Event:572398.655GCheapafter HeapafterGCinvocations=5048(full357): PSYoungGentotal201472K,used1998K[0x573c0000,0x63bc0000,0x63bc0000) edenspace198144K,0%used[0x573c0000,0x573c0000,0x63540000) fromspace3328K,60%used[0x63880000,0x63a73830,0x63bc0000) tospace3328K,0%used[0x63540000,0x63540000,0x63880000) ParOldGentotal843776K,used423703K[0x23bc0000,0x573c0000,0x573c0000) objectspace843776K,50%used[0x23bc0000,0x3d985cc0,0x573c0000) PSPermGentotal262144K,used51848K[0x03bc0000,0x13bc0000,0x23bc0000) objectspace262144K,19%used[0x03bc0000,0x06e62138,0x13bc0000) } Event:576881.689GCheapbefore {HeapbeforeGCinvocations=5049(full357): PSYoungGentotal201472K,used200142K[0x573c0000,0x63bc0000,0x63bc0000) edenspace198144K,100%used[0x573c0000,0x63540000,0x63540000) fromspace3328K,60%used[0x63880000,0x63a73830,0x63bc0000) tospace3328K,0%used[0x63540000,0x63540000,0x63880000) ParOldGentotal843776K,used423703K[0x23bc0000,0x573c0000,0x573c0000) objectspace843776K,50%used[0x23bc0000,0x3d985cc0,0x573c0000) PSPermGentotal262144K,used51850K[0x03bc0000,0x13bc0000,0x23bc0000) objectspace262144K,19%used[0x03bc0000,0x06e62850,0x13bc0000) Event:576881.696GCheapafter HeapafterGCinvocations=5049(full357): PSYoungGentotal201472K,used3155K[0x573c0000,0x63bc0000,0x63bc0000) edenspace198144K,0%used[0x573c0000,0x573c0000,0x63540000) fromspace3328K,94%used[0x63540000,0x63854cb0,0x63880000) tospace3328K,0%used[0x63880000,0x63880000,0x63bc0000) ParOldGentotal843776K,used423703K[0x23bc0000,0x573c0000,0x573c0000) objectspace843776K,50%used[0x23bc0000,0x3d985cc0,0x573c0000) PSPermGentotal262144K,used51850K[0x03bc0000,0x13bc0000,0x23bc0000) objectspace262144K,19%used[0x03bc0000,0x06e62850,0x13bc0000) } Event:580535.452GCheapbefore {HeapbeforeGCinvocations=5050(full357): PSYoungGentotal201472K,used201299K[0x573c0000,0x63bc0000,0x63bc0000) edenspace198144K,100%used[0x573c0000,0x63540000,0x63540000) fromspace3328K,94%used[0x63540000,0x63854cb0,0x63880000) tospace3328K,0%used[0x63880000,0x63880000,0x63bc0000) ParOldGentotal843776K,used423703K[0x23bc0000,0x573c0000,0x573c0000) objectspace843776K,50%used[0x23bc0000,0x3d985cc0,0x573c0000) PSPermGentotal262144K,used51856K[0x03bc0000,0x13bc0000,0x23bc0000) objectspace262144K,19%used[0x03bc0000,0x06e64228,0x13bc0000) Event:580535.459GCheapafter HeapafterGCinvocations=5050(full357): PSYoungGentotal200960K,used1858K[0x573c0000,0x63bc0000,0x63bc0000) edenspace197632K,0%used[0x573c0000,0x573c0000,0x634c0000) fromspace3328K,55%used[0x63880000,0x63a50be0,0x63bc0000) tospace3584K,0%used[0x634c0000,0x634c0000,0x63840000) ParOldGentotal843776K,used423703K[0x23bc0000,0x573c0000,0x573c0000) objectspace843776K,50%used[0x23bc0000,0x3d985cc0,0x573c0000) PSPermGentotal262144K,used51856K[0x03bc0000,0x13bc0000,0x23bc0000) objectspace262144K,19%used[0x03bc0000,0x06e64228,0x13bc0000) }
看了上面的内容,并没有发现tomcat闪崩是由于老年代,持久代,新生代空间不足引起的。有好几次因为eden区空间使用到100%引起的fullgc,但是垃圾回收过后eden区的空间都恢复到正常的水平。
日志中还记录了tomcat闪崩时heap堆的使用情况:
Heap PSYoungGentotal200960K,used95671K[0x573c0000,0x63bc0000,0x63bc0000) edenspace197632K,47%used[0x573c0000,0x5cf5d230,0x634c0000) fromspace3328K,55%used[0x63880000,0x63a50be0,0x63bc0000) tospace3584K,0%used[0x634c0000,0x634c0000,0x63840000) ParOldGentotal843776K,used423703K[0x23bc0000,0x573c0000,0x573c0000) objectspace843776K,50%used[0x23bc0000,0x3d985cc0,0x573c0000) PSPermGentotal262144K,used51856K[0x03bc0000,0x13bc0000,0x23bc0000) objectspace262144K,19%used[0x03bc0000,0x06e64228,0x13bc0000)
一切都那么正常,同时又那么诡异。
翻看了之前发生日志,内容都是大同小异。
重新翻看了几遍日志,这次把重点放在日志中建议的解决方案上:
#Reducememoryloadonthesystem #Increasephysicalmemoryorswapspace #Checkifswapbackingstoreisfull #Use64bitJavaona64bitOS #DecreaseJavaheapsize(-Xmx/-Xms) #DecreasenumberofJavathreads #DecreaseJavathreadstacksizes(-Xss)
其中下面几个解决方案不采用:
- Reducememoryloadonthesystem。系统内存够用,32G的内存,还剩20G没用,无需减少内存。
- Increasephysicalmemoryorswapspace。系统内存够用,32G的内存,还剩20G没用,无需增加物理内存。
- Use64bitJavaona64bitOS。32位操作系统,无法使用64位jdk。
只剩下下面的三个解决方案了:
- DecreaseJavaheapsize(-Xmx/-Xms)。heap堆设置过大,就会影响剩余内存。
- DecreasenumberofJavathreads
- DecreaseJavathreadstacksizes(-Xss)
而减少java线程的数量,需要修改代码,这个也不实际。
最后只剩下
- DecreaseJavaheapsize(-Xmx/-Xms)
- DecreaseJavathreadstacksizes(-Xss)
这两个解决方案了,就从这里入手,曙光就在前方。
先看DecreaseJavathreadstacksizes(-Xss)解决方案
java线程运行也是需要内存空间的,-Xss参数指定每个线程堆栈的大小,为jvm启动的每个线程分配的内存大小。在jdk1.4版本中是256K,JDK1.5及以上版本是1M。
tomcatjvm的参数设置如下:
JAVA_OPTS=%JAVA_OPTS%-server-Xms1024m-Xmx1024m-Xmn200M-XX:PermSize=256M-XX:MaxPermSize=512m-XX:SurvivorRatio=1-Xss256k
已经通过-Xss设置每个java线程堆栈的大小为256K。
在java语言里,当你创建一个线程的时候,虚拟机会在JVM内存创建一个Thread对象同时创建一个操作系统线程,而这个系统线程的内存用的不是JVMMemory,而是系统中剩下的内存(MaxProcessMemory-JVMMemory-ReservedOsMemory)。
当需要创建线程,而操作系统剩余内存不够分配给一个java线程时,就会报OutofMemoryError的错误。
由于已经设置通过-Xss设置java线程栈的大小为256K,因此也决定不采用这个解决方案。
现在只剩下DecreaseJavaheapsize(-Xmx/-Xms)这个解决方案了。通过减少堆的大小,而留出足够的内存空间给java线程堆栈使用。
32位的window操作系统给每个进程分配的内存空间是2G,减去堆的最大容量和PermSize的最大容量,剩下的容量就留给java线程栈使用。
经过分析代码和之前错误的日志,发现一般在350个线程这样就出现OutofMemoryError的错误。
在出现错误时,heap空间才用了不到40%。因此决定将javaheap的从1G减少到768M。
修改的jvm参数如下:
JAVA_OPTS=%JAVA_OPTS%-server-Xms768m-Xmx768m-Xmn200M-XX:PermSize=256M-XX:MaxPermSize=512m-XX:SurvivorRatio=1-Xss256k
到目前为止,系统已经稳定运行1个月,各个参数指标都在正常范围内。heap使用率最高才70%。
总结:
1、经过这次解决bug,加深了对java虚拟机的了解,特别是线程栈,内存堆,持久代,新生代等概念。
2、一定要仔细阅读日志文件,一步一步排除掉潜在的解决方案。综合系统的运行环境,找出合理的解决方案。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。