03.大堆问题定位
- 首先确定是内存问题,还是内存泄露。如果是内存问题,则可以通过JVM监控等手段,判断内存持续增长的区域;假设确定了是内存泄露,也可能是堆内或者堆外
- 堆内内存泄露的情况,最有效的手段莫过于Heap Dump,使用
jmap -heap $PID、jcmd或者HeapDumpOnOutOfMemoryError等等手段都可以获取,使用MAT找超级powerful的机器分析;MAT 其实提供了分析脚本可以在不用 IDE 加载整个 HEAP 获取到需要的信息, 也就是通过脚本解析 HEAP 逐步分析, 具体可参考 How to Analyse Large Heap Dumps. PS: 曾经分析过 108G 的堆 - 实际生产中,这么大的堆,不管是dump对生产系统的影响,还是dump本身的难度,都往往不切实际,相对低成本的手段:
jmap -histo $PID或jmap -histo:live $PID获得当前堆内对象的个数统计,并采样多次,查看一下里面object的分布,看哪类对象比较多,一般来说200G的堆,做一次jmap -histo可能要几十秒到一分钟,可能会触发full gc,如果使用CMS或G1的话, 可加入-XX:+ExplicitGCInvokesConcurrent使用并发收集器显式;如果不能探明的话, 只能使用 jmap 将整个堆 dump 下来。
所以如果有类似timeout killer的守护线程,要注意不要让它把进程kill掉- 详细的GC日志等,比如判断引用堆积情况等,看看没有没什么异常, 比如有没有因为metaspace 满了而导致的GC。这种情况,可以看看是不是打开XX:+TraceClassLoading -XX:+TraceClassUnloading 分析下类加载情况。
- 使用Tencent JDK,可以利用old object sampling技术,不做Heap dump定位相当一部分memory leak
- 堆外内存: 确认下是否是 Java 的 direct bytebuffer 泄露, 由于采用 reference 机制回收, 如果一直没有触发 JVM GC 或回收线程偏少也会导致堆外内存回收缓慢导致泄露
如果是 Native 或者使用 Unsafe 方式直接向 OS 申请内存, 可通过 NMT以及 pmap 等查看, JDK 团队分享的 http://km.oa.com/group/42239/articles/show/404478?ts=1574932416 可谓是面面俱到.
使用MAT命令行分析
How to Analyse Large Heap Dumps
下载MAT
到MAT的安装目录下,打开
MemoryAnalyzer.ini,调整MAT的启动jvm参数1
2
3
4
5
6
7-Xms6144m
-Xmx8192m
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled
-XX:+CMSClassUnloadingEnabled
-XX:+UseCMSInitiatingOccupancyOnly堆最大大小调整为机器内存大小
dump文件所在文件夹,确保有dump文件两倍的空间
到MAT的安装目录下,使用root账户运行命令
For UNIX:1
2
3./ParseHeapDump.sh /opt/heap_dump/jvm.hprof org.eclipse.mat.api:suspects
./ParseHeapDump.sh /opt/heap_dump/jvm.hprof org.eclipse.mat.api:overview
./ParseHeapDump.sh /opt/heap_dump/jvm.hprof org.eclipse.mat.api:top_componentsFor Windows:
1
2
3ParseHeapDump.bat D:\heap_dump\jvm.hprof org.eclipse.mat.api:suspects
ParseHeapDump.bat D:\heap_dump\jvm.hprof org.eclipse.mat.api:overview
ParseHeapDump.bat D:\heap_dump\jvm.hprof org.eclipse.mat.api:top_components使用MAT打开生成的分析结果文件