java 内存溢出 jstack,JVM内存溢出(OOM)的问题

线上的应用运行几天后,总是出现卡死甚至出现OOM的情况

通过Linux的top命令查看cpu占比

首先通过top命令查看,发现某个java程序占用了较高内存:

729f6d2c5bdc5e08504ce87111c98987.png

JDK的jps命令确定是哪个java程序

然后通过jps -l 与上面的PID列(2848)比较,确定是 picasso-java-v1.jar 这个java程序占用cpu过高:

5c478987da213b738527755478ee18d7.png

通过ps 查看具体哪个JVM线程

当时想的是可能应用内某个线程导致死循环,使用如下命令查看2848进程的各个线程小号cpu时间//ps -mp [线程号] -o THREAD,tid,time

ps -mp 2848 -o THREAD,tid,time

下图 %CPU列 为 cpu的百分比,TID列 为线程id

b451f61304455332b8b023b222912fa7.png

找到消耗cpu最大的线程(当时线上出现时某个线程消耗cpu90%多),这里为了演示,所以取2858这个线程。

通过jstack查看java中的具体线程栈信息

然后把上面线程id转化为16进制,在shell中使用printf “%x\n” tid即可,结果为b2a:

ae328428d7d67696da95afcc6b065ef0.png

然后使用jstack输出这个线程的调用栈://jstack [进程id] | grep [线程的16进制id] -A行数

jstack 2848 | grep b2a -A30

9477ef59f33d14a6272566e9426320e4.png

发现为GC线程,原来是jvm内存回收导致的cpu过高!

通过jstat查看内存回收情况

使用jstat -gcutil 线程数 间隔秒数 次数命令查看:

070a664359b943e10e375b76168559ea.png

如图上面的FGC列Full GC次数为几百,而FGCT的Full GC秒数达到了几千,通过设置更多的监控次数观察,每次Full GC过后,O列的老年代还是99%,可见是内存不足导致的一直不停Full GC !

重启程序,使用-Xmx -Xms设置更大堆内存

通过重启程序,-Xmx2048m -Xms2048m设置了更大的内存参数,缓解了问题!

问题重现,寻找其他原因,使用jmap生成堆转储文件

隔了几天后,问题重现,此时通过jmap 生成了镜像jmap -dump:format=b,file=dumpfile.dat [pid]

生成的文件也是非常之大,达到2.1Gb!

柳暗花明,使用Eclipse Memory Analyzer分析出原因

把dump文件下载到本地,同时下载了Eclipse Memory Analyzer对dump文件进行分析。

在Eclipse Memory Analyzer中生成Leak Suspects报告:

7ef8dd3f8a66903d42061c656fd8fd8a.png

发现是 PoolingHttpClientConnectionManager 这个类导致的。再点击上图中的Details,查看详细信息:

7f17e83ddae942e04ab89677a7dfb9ce.png

这下清晰了,是阿里的oss类库导致的,结合程序中的如下代码:

1

2OSSClient ossClient = new OSSClient("","");

PutObjectResult putObjectResult = ossClient.putObject("", "", "");

这个方法在程序中没有使用单例模式而且没有关闭,每调用一次就生成了一个PoolingHttpClientConnectionManager,而且是不可回收的。通过源码查看到IdleConnectionReaper.size()这个类会生成PoolingHttpClientConnectionManager的总数量。

验证猜测

使用 -Xms20m -Xmx20m 运行以下程序,发现size一直变大,最后导致OOM (java.lang.OutOfMemoryError)

1

2

3

4

5

6

7for (int i = 0; i < 60000; i++) {

OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

ossClient.putObject("", "test1234" + UUID.randomUUID(), new File("d:/file.txt"));

System.out.println("size="+IdleConnectionReaper.size());

Thread.sleep(2);

}

4901bfddda621275523f855a6b449ba2.png

查看api,得知使用shutdown方法即可关闭OSSClient:

1ossClient.shutdown();

再运行以下程序,size一直为0,一切正常:

1

2

3

4

5

6

7for (int i = 0; i < 60000; i++) {

OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);

ossClient.putObject("", "test1234" + UUID.randomUUID(), new File("d:/file.txt"));

ossClient.shutdown();

System.out.println("size="+IdleConnectionReaper.size());

Thread.sleep(2);

}

至此,终于找到了导致cpu过高和OutOfMemoryError的真凶!

原文链接:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值