前言:
此刻你们对“tomcat heapdump”大致比较关注,看官们都想要了解一些“tomcat heapdump”的相关内容。那么小编在网摘上网罗了一些有关“tomcat heapdump””的相关知识,希望大家能喜欢,同学们一起来学习一下吧!现象
在日常系统巡查系统监控时发现消息通知系统CLOSE_WAIT监控不正常,如下图:
排查过程
登录机器通过命令查看CLOSE_WAIT状态链接信息,发现是由本地发起的连接,调用外部接口,端口是80,一般情况下应该是HTTP调用
netstat -nt | grep CL
再确认下CLOSE WAIT在TCP状态转移中所处的阶段,IBM Documentation
从图中看,CLOSE_WAIT是被动关闭方的状态,被动关闭方调用close后向主动关闭方发送FIN后进入LAST_ACK状态。
至此,怀疑是应用内有http请求没有关闭造成了泄漏。瞅准时机登录机器线上dump
sudo -u tomcat jmap -dump:format=b,file=/tmp/heap.hprof 3033
发现大量unreachable_objects里面相关的对象KeepAliveStream,达到10712个
分析KeepAliveStream和其引用它的对象,分析下来是由大量的HtmlEmail引用持有:
抽样分析看有432个MimeBodyPart对象,其类为URLDataSource的属性url_conn持有:
进一步查看URL对象信息都是固定url下的邮件发送的附件信息:
问题分析
到这里,已经从dump中分析出是邮件发送时造成了链接泄漏的场景。分析Apache Common Email源代码了解发送邮件的过程,为方便理解简单划分为2步骤
1.attach附件过程会创建URLDataSource(url),首先会校验文件是否存在,打开并关闭连接。
package org.apache.commons.mail;public class MultiPartEmail extends Email {/*...省略...*/ public MultiPartEmail attach(URL url, String name, String description, String disposition) throws EmailException { try { InputStream is = url.openStream(); is.close(); } catch (IOException var6) { throw new EmailException("Invalid URL set:" + url, var6); } return this.attach((DataSource)(new URLDataSource(url)), name, description, disposition); }/*...省略...*/}
2.发送邮件 先获取附件的ContentType,然后获取文件流。其中获取ContentType调用的方法是DataHandler.getContentType,执行后openConnection会赋值给URLDataSource.url_conn属性。获取文件流后的InputStream之后finally里面直接关闭。
package javax.activation;public class DataHandler implements Transferable {/**...省略...**/ public String getContentType() { return this.dataSource != null ? this.dataSource.getContentType() : this.objectMimeType; }public void writeTo(OutputStream os) throws IOException { if (this.dataSource != null) { InputStream is = null; byte[] data = new byte[8192]; is = this.dataSource.getInputStream(); int bytes_read; try { while((bytes_read = is.read(data)) > 0) { os.write(data, 0, bytes_read); } } finally { is.close(); is = null; } } else { DataContentHandler dch = this.getDataContentHandler(); dch.writeTo(this.object, this.objectMimeType, os); } }/**...省略...**/}
由此可看,url_conn没有被关闭滞留成为不可达对象了。ng侧超时会主动关闭连接而客户端侧没有发起close方法导致了CLOSE_WAIT状态的持续增长。
另外还遗留两个问题:
1.为啥CLOSE_WAIT呈锯齿状有陡然下降的趋势?
对象都是unreachable_objects,会不会是GC导致的呢?查看PS_MarkSweep监控看完全对的上趋势
那么此时信息已经闭环了。FGC时会调用finallize方法里面会有close等资源释放,close方法调用后回归到图3的状态转移,CLOSE_WAIT量会掉下来。
2.为啥之前没有CLOSE_WAIT的持续增长问题?
分析发现此次case出现是由于一个邮件有400+附件需要发送,发送过程是串行的导致超大对象存活时间超长晋升到了老年代。之前邮件附件数量少,在YGC侧时就已经回收了。
问题解决
翻看Apache common email组建没找到如何提ISSUE(哪位大佬知道可指导一下嘛?)
代码已经提交在github上 GitHub :
标签: #tomcat heapdump