记⼀次java应⽤内存不断增长问题解决过程
1、在解决问题之前,先看下jvm堆内存结构,如下图所⽰:
对于Java应⽤,虚拟机管理的内存,可以参考如下图所⽰:
⼀般对于⼀个应⽤来说,如果内存使⽤过⼤,可以从两块来分析,第⼀:堆内存,第⼆:堆外内存。下⾯着重从这两个⽅⾯阐述下对最近遇到的内存问题进⾏分析并解决的过程。java下载过程
问题详情:
本⼈负责了⼀个应⽤,主要就是定时调度⼀批接⼝任务,同时提供⼀些对外接⼝,功能很简单,在使⽤的过程中,逐渐发现了⼏个内存问题。
1、oom问题,发现⼀段时间后,应⽤进程就没了,查看⽇志提⽰内存溢出,内存被吃光了,不够分配下个资源使⽤的空间了。
0x80070057错误原因2、第⼀个问题解决后,添加了⼏个功能,发现⼜出现⼀些问题:应⽤运⾏⼀段时间后,发现,内存随着时间不段推移,内存占⽤也越来越多,⼀般4天后,内使⽤⼏乎达到90%,很吓⼈。
第⼀个问题解决过程:
oom问题,⼀般就是程序代码处理不当造成的,有些资源没有释放或者⼀直在循环开辟某些内存资源。分析这个问题,我使⽤了本地jvm的⼀个⼯具,到对应的jdk/bin/jvisualvm ⼯具,它是⽤来JVM监测、故障排除、分析⼯具,主要以图形化界⾯的⽅式提供运⾏于指定虚拟机的Java应⽤程序的详细信息。
1、本地启动程序后,同时打开jvisualvm,在jvisualvm⼯具可视化页⾯有多个选项tab,可以根据需求,选择对应的tab进⾏使⽤,⽽我主要对profiler进⾏了使⽤,同时发现了问题,进⽽解决了问题,如下图所⽰:
这⾥排查问题,主要对快照功能进⾏了使⽤,每隔⼀段时间进⾏⼀次快照(每次快照前,先进⾏⼀次⼿动gc),然后两次快照进⾏对⽐差值,进⽽发现内存不断增长的是哪些对象。
数字华容道下载如下图所⽰,选择第⼀张和最后⼀张对⽐图:
卸载hadoop通过以上⽅法,不断去尝试,⼀般⼏个⼩时候,就能对⽐出哪些是⼀直占⽤内存,且gc也不⽣效的对象,从⽽定位类名以及⽅法,去解决即可。下图是解决后内存的使⽤情况:
运⾏个半个⽉是没有问题的,后⾯也没有出现oom。
第⼆个问题解决过程
第⼆个问题,不好定位,解决的时间⽐较长,需要长时间不断观察,才会彻底解决。内存⼀直再涨,
但是涨的速度并没有很快,所以⽤第⼀种⽅法不⼤好分析,因为本地数据并不多,不好分析。通过后⾯的分析结果,这⾥涉及两块内存:堆内存,堆外内存。
这次内存不断增长问题,并没有导致应⽤不可⽤,出现oom等现象,纯粹是⾃⼰隔段时间观察的,下⾯是解决问题的过程。
1、jvm参数优化
应⽤运⾏⼀段时间,对应⽤java堆内存的使⽤进⾏观察,这⾥要清楚堆内存结构,通过命令:
jmap -heap pid,可以查看堆内存使⽤情况,⽐如:
排查内存问题,jvm优化也能解决⼀部分问题,所以我先通过优化,再进⼀步观察,优化的原则,可以参考下图:
通过jvm优化,在gc时间可接受的情况下,配置jvm参数,在观察⼀段时间。
2、堆内存使⽤分析
通过jvm优化后,使⽤命令 jstat -gc pid,查看gc次数,发现gc时间相⽐之前⾼了⼀些,因为应⽤对实
时性要求不⾼,可以接受,xmx配置⽐之前多了⼀倍,应⽤运⾏时间长了很多,但是还是会发现,内存在涨的问题,⾃⼰继续分析。
通过top命令发现,运⾏⼏天后,res内存超过配置的xmx,随着时间推移,res还是再涨。通过jmap -heap pid命令查看堆内存使⽤情况,发现堆内存占⽤并不多,远⼩于res值,说明这⾥res的内存值多半是堆外内存在占⽤,于是开始转战对堆外内存的分析。
3、堆外内存分析
在分析堆外内存时,在⽹上搜了很多。在分析堆外内存时,使⽤到了jcmd命令,Native Memory Tracking (NMT) 是Hotspot VM⽤来分析VM内部内存使⽤情况的⼀个功能。我们可以利⽤jcmd(jdk⾃带)这个⼯具来访问NMT的数据。
a、在使⽤jcmd之前,需要配置⼀些jvm参数,如下如所⽰:
即:-XX:NativeMemoryTracking=[off | summary | detail] 配置该jvm参数,重启应⽤即可。
cancel是什么意思车上的功能b、通过以上操作,使⽤命令 jcmd pid VM.native_memory detail scale=MB > a.txt 查看内存的⼀个使⽤情况,如下图所⽰:
这⾥包含了⼏个部分,在分析这些内存的时候,需要搞明⽩啥意思,具体可以参考⽂章:
blazing怎么读
c、搞清楚每⼀项代表的含义,那接下来就是结合⾃⼰的代码,分析哪块可能有问题。对于我问题,通过 jcmd pid VM.native_memory baseline 设置基准线,耐⼼多次 jcmd pid VM.native_memory detail.diff scale=MB > 看差值,然后得出有可能哪⾥是问题所在,⽐如下⾯是差值的⼀个截图,这是未解决问题前的⼀个⽐较,问题很明显:
从这⾥就可以看出,线程⼀直在增加,且资源⼀直未释放,但是有哪些线程⼀直在创建申请资源呢,⼀时也摸不着头绪,所以要知道哪些线程,需要进⼀步分析。
d、在分析以上问题时,需要查看dump⽂件,可以通过Jprofile 或者 eclipse mat来分析内存占⽤情况。
通过jmap -dump pid > m.hprof 发现⽂件实在是太⼤,达到3g多,⽽且还占⽤着内存,根本从linux上转移不出来,也就⽆法通过本地idear插件Jprofile 查看,于是使⽤⼯具MemoryAnalyzer,根据系统属性,官⽹下载后,解压即可。
官⽹地址:
下载后将包传到linux服务器上解压。
MemoryAnalyzer.ini 配置⽂件可以修改最⼤的内存,默认1G基本够⽤了。
我在使⽤后,发现java不可⽤,所以在上⾯基础上需要配置下⾯的两部分:-vm -startup,下⾯是我后来添加的配置:
然后,在linux执⾏分析命令执⾏命令
.
/ParseHeapDump.sh m.hprof lipse.mat.api:lipse.mat.api:overview
m.hprof就是jvm的dump⽂件,在mat⽬录下会⽣成3份.zip结尾的报告和⼀些m.相关的⽂件,将⽣成的m.hprof相关的⽂件都下载到本地磁盘:
1)使⽤浏览器
解压缩以.zip结尾的⽂件,解压后
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论