关于Java Tomcat 内存溢出排查 |
您所在的位置:网站首页 › 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 的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 分钟。。 全局搜索各种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 即可。 昨晚把 队列 换成了阿里的队列,问题解决了,几个小时过去了,还是5.6% 。 换成了阿里的 队列 ,我把队列用于本地计算机跑,线上跑网站,把所有队列的错误信息,以及执行情况看了一下,发现是之前逻辑写的有问题,导致队列异常,队列异常没有catch及时处理。故导致了这个现象的最大的罪魁祸首。 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |