龙空技术网

CLOSE_WAIT过多问题

jsaxon 110

前言:

此刻你们对“tomcat heapdump”大致比较关注,看官们都想要了解一些“tomcat heapdump”的相关内容。那么小编在网摘上网罗了一些有关“tomcat heapdump””的相关知识,希望大家能喜欢,同学们一起来学习一下吧!

现象

在日常系统巡查系统监控时发现消息通知系统CLOSE_WAIT监控不正常,如下图:

图1

排查过程

登录机器通过命令查看CLOSE_WAIT状态链接信息,发现是由本地发起的连接,调用外部接口,端口是80,一般情况下应该是HTTP调用

netstat -nt | grep CL

图2

再确认下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