一次线上JVM进程OOM问题排查
问题描述
线上查询设计服务每运行一段时间就会抛oom异常,然后服务不可用,重启后恢复。
服务异常之前通过cat监控发现会频繁full gc,gc后释放内存很小,我们推断发生了内存泄漏。
相关工具:ps、jmap、mat、top、jps、jstack
解决步骤
(1)找到进程pid
由于已经知道哪个服务出了什么问提,跳过jps,top等步骤直接ps ps aux | grep panther-query
(2)查看推内存使用情况
jmap -heap pid
(3)查看内存实例占用情况
jmap -histo:live pid | more
定位到几个异常的类实例特别多,占用内存很大超过500MB
(4)生成dump文件
jmap -dump:live,format=b,file=data.dump pid
(5)使用mat分析dump
/path/ParseHeapDump.sh data.dump org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components
会生成三个zip文件 heap-dump_System_Overview.zip heap-dump_Leak_Suspects.zip heap-dump_Top_Components.zip
(6)解压zip后查看分析文档
主要分析Leak_Suspects找到可能会引起内存泄漏的点
(7)结合代码分析发现几处异常
7.1 StringBuilder char[] 问题:
频繁deleteCharAt 和append导致的扩容(array copy) 方案:new StringBuilder指定容量,默认16KB(16384),append只增不删(尽量避免使用deleteCharAt)
7.2 String[][] String 问题:
频繁创建10w行*n列大数组 方案:减少中间结果占用内存,直接ResultSet到Hdfs,取消使用大数组
7.3 FileSystem.Cache 问题:
内存泄漏,同时并持有的Configuration也没有释放,占用过高的内存 原因使用代理生成的ugi,每次都是new的,导致FileSystem每次都是new实例,代码里有没有close,所以泄露了 方案:FileSystem放入try catch块,自动close
(8)最后jvm参数配置oom时打印dump日志
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/project/heapdump.hprof