关于Java Tomcat 内存溢出排查

您所在的位置:网站首页 tomcat占内存太大 关于Java Tomcat 内存溢出排查

关于Java Tomcat 内存溢出排查

2024-02-23 16:39| 来源: 网络整理| 查看: 265

网站不知道什么时候,开始内存飙升,从   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  的pid

  ps -aux|grep xxx-tomcat

 

 

然后执行jmap命令:

  jmap -dump:format=b,file=73630.hprof 16706

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

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

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

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

从配置文件web.xml 查看,发现   Session    超时配置了900 分钟。。。醉了,回想起来,是当时因为有权限校验(防止攻击)模块利用   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);//大对象用完赋值nullbo = 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 即可。

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

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

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



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3