java内存溢出让tomcat停止,关于Java Tomcat 内存溢出排查心得分享

我网站不知道什么时候,开始内存飙升,从   Tomcat  启动后,初始内存占用4%~5%左右,到20%、40%最后服务器卡死,SSH都连不上服务器,不得不重启。但是我知道是我程序的问题。然后分析问题,解决问题。陆陆续续持续了一个多月,下面分享解决思路。

一、定位造成内存溢出可能存在的问题io流操作文档没关闭流。

往一个静态集合变量里一直压栈。

连接没释放。

Java队列没消耗。

Ehcache缓存使用量过大。

频繁IO操作大文件。

Session过期时间太久。

等等.....

我定位有可能造成的原因是以上原因,针对本站的特点在做细排查,有可能出现的问题。io流操作文档没关闭流。(有可能)

往一个静态集合变量里一直压栈。(没有这个问题)

链接没释放。(有可能,因为本站有大量的HttpClient请求)

Java队列没消耗。(有可能,因为本站使用上了)

Ehcache缓存使用量过大。(没使用)

频繁IO操作大文件。(没有)

Session过期时间太久。(可能有)

等等.....

二、采用Memory Analyzer Tool(MAT)分析Java内存

采用   jmap  命令(Java Memory Map)导出内存转储快照(Dump);

首先查询到你对应的   Tomcat  的pidps -aux|grep xxx-tomcat

fc37357767951873733b9c0c451abeb3.png

然后执行jmap命令:jmap -dump:format=b,file=73630.hprof 16706

e1e5bd6616442e929bf3f30e67f86adf.png

导出完毕。down下来用   Eclipse  ,或者   MyEclipse  查看,但是 MyEclipse或者 Eclipse要先安装工具,自行百度。然后以openFile的方式打开。如图:

fe000a5eaa5265920c4edc609f6ec97a.png

可能有点看不懂,自行解决,点击Histogram,可以看到内存中的详细信息。

a96e634d7e0b098e56a47a9b7cb8bfe9.png

可以看到char[]、byte[]占用的是最多,而且不是多一点点。这明显不正常。就是一些IO流相关的信息。Memory Analyzer 工具还是有很多功能的,我也不太会用。具体可以多看看相关的博客。下面来排查问题。

三、问题逐一排查,由容易到复杂

3.1 Session检查

从配置文件web.xml查看,发现   Session    超时配置了900分钟。。

e039b1f337d8971c7c409d6e4335840f.gif。醉了,回想起来,是当时因为有权限校验(防止攻击)模块利用   Session  来实现,所以才出此下策。改成30分钟,重启后效果有一点点。继续排查。

3.2 IO流操作没关闭检查(严重)

全局搜索各种InputStream、OutputStream,各种Buffer等等,然后各种修改关闭。尤其是本站的  HTTP  模拟请求工具,一天的用量非常大。如下IO流在finally里try...catch各种关闭。try{

//......

}catch(Exception e){

//......

}finally{

realUrl = null;

try {

if(null != conn)conn.disconnect();

conn = null;

} catch (Exception e2) {

LoggerUtils.fmtError(HttpManager.class, e2, "请求完毕关闭流出现异常,可以忽略![%s]", url);

}

try {

if(null != outStream)outStream.close();

outStream = null;

} catch (Exception e2) {

LoggerUtils.fmtError(HttpManager.class, e2, "请求完毕关闭流出现异常,可以忽略![%s]", url);

}

try {

if(null != out)out.close();

out = null;

} catch (Exception e2) {

LoggerUtils.fmtError(HttpManager.class, e2, "请求完毕关闭流出现异常,可以忽略![%s]", url);

}

try {

if(null != inStream)inStream.close();

inStream = null;

} catch (Exception e2) {

LoggerUtils.fmtError(HttpManager.class, e2, "请求完毕关闭流出现异常,可以忽略![%s]", url);

}

try {

if(null != in)in.close();

in = null;

} catch (Exception e2) {

LoggerUtils.fmtError(HttpManager.class, e2, "请求完毕关闭流出现异常,可以忽略![%s]", url);

}

double end = System.currentTimeMillis();

map.put("time", (end - begin) / 1000);

//大对象用完赋值null

bo = null;//促进回收

}

结论:全部加好后重启,过一段时间再看。效果不明显。其实是我在平时代码严谨上这个错误没有出现,但是从经验角度来说,如果这个没处理好,这个是最容易出现内存溢出的。

ps:关于后面有一段代码,bo=null;//促进回收,我个人是这么理解,不知道有没毛病。主要是针对局部变量的大变量。可以用完后赋值为null。

3.3 HttpClient请求链接释放问题(严重)

Httpclent  请求链接不主动关闭,这个问题也是个大问题,但是对内存的影响,看从什么角度,占用最大的应该还是响应链接,把链接用完了,新的链接就进不来,我们知道  Tomcat  默认配置一共好像才150个,一会就用完了,如果不用完关闭,那么会造成链接释放慢,甚至不释放。如果不释放,请求得到的responseBody那么有可能一直没有释放了。

HttpClient怎么释放?

其实百度一下有很多答案,我这里顺便带一下。

1.请求头增加关闭Head信息。//添加头信息

HttpURLConnection conn = null;

URL realUrl = new URL(url);

// 打开和URL之间的连接

conn = (HttpURLConnection) realUrl.openConnection();

//省略部分代码

//增加请求完毕后关闭链接的头信息

conn.setRequestProperty("Connection", "close");

2.用完Httpclent后手动关闭//添加头信息

HttpURLConnection conn = null;

URL realUrl = new URL(url);

// 打开和URL之间的连接

conn = (HttpURLConnection) realUrl.openConnection();

//省略部分代码

//手动释放

if(null != conn)conn.disconnect();

3.通过线程的方式扫描关闭。

这个方法其实类似启动一个守护线程(一直启动着),来扫描有没有关闭的请求。这个方法比较鸡肋,用的好就很好,用的不好就蛋痛了。推荐使用方法一、方法二,为了保险起见你可以两种一起使用,并不会有问题。

总结:  Httpclent  链接请求完毕一定要关闭。有的人可能会看了浏览器里的请求头信息为:Connection:keep-alive,这个回头我会详细说明,但是这个是浏览器需要的,因为你还要继续加载css、js、image等等,大概是这个意思,而你的  Httpclent  只需要加载一次,所以直接close即可。

0fUKlnh3OETvEAAAAASUVORK5CYII=

3.3 Java队列(最终问题定位)

昨晚把  队列  换成了阿里的队列,问题解决了,几个小时过去了,还是5.6%。

c5bb2abf91173faf981ab72e8cf76195.png

换成了阿里的  队列  ,我把队列用于本地计算机跑,线上跑网站,把所有队列的错误信息,以及执行情况看了一下,发现是之前逻辑写的有问题,导致队列异常,队列异常没有catch及时处理。故导致了这个现象的最大的罪魁祸首。

如果本文对你有帮助,那么请你赞助我,让我更有激情的写下去,帮助更多的人。

¥我需要走的更远,点击我 赞助。

如果还有疑问,点击我加群,为你提供最好的解答。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值