使用Eclipse Memory Analyzer(MAT)解决Java Web应用故障

这周一我负责维护的一个系统突然出现故障,用户反映系统很慢,浏览器加载半天最后进了错误页面。登录到系统查看,发现系统负载很高,用sar命令进一步明确了是java进程在某一时刻后突然占据了大量CPU资源。查看应用日志,发现了OutOfMemory异常,然后使用jstat的gcutil确认JVM heap工作不正常,已经有上千次的full gc了。

用VisualGC或者jmap工具生成Headp dump文件,然后用MAT打开,生成内存泄露的分析报告,查找占用内存最大的对象并通过Thread call stack就可以定位导致问题出现的类和方法。

由于系统属旧系统,代码一直没有改动,显然是数据出了问题。最终发现是用户输入的格式不规范的数据,使得对数据做查找替换的一段代码不断地扩大1个字符串对象,导致内存消耗不断增长,gc频繁。


# sar
# sar -P ALL 1 5
# jstat -gcutil 1000
# jmap -dump:format=b,file=HeapDump.hprof

参考: http://wiki.eclipse.org/index.php/MemoryAnalyzer

再谈使用Commons HttpClient产生大量CLOSE_WAIT

默认情况下(即用默认构造方法建立HttpClient对象),httpMethod.releaseConnection()方法并不会关闭连接的Socket,目的是可以重用这个连接。网上有不少讨论:

这些讨论里提出了关闭Socket的方法,即在请求头里加上"Connection: close",具体可见HTTP协议规范里的说明

其实HttpClient 3.x 里还可以通过下面的方式建立HttpClient对象,来保证关闭Socket连接:

HttpClient httpClient = new HttpClient(new SimpleHttpConnectionManager(true));

具体可见HttpClient源码和API文档。

当然,使用HttpClient的多线程连接管理器时就不用这么做了。

遭遇”java.io.IOException: Too many open files”

今天下午,线上的Jetty服务宕机了,日志里大量的"java.io.IOException: Too many open files"错误。搜索到Freddy Chu的博客里正好有关于此的解决办法。

$ sysctl fs.file-max
$ lsof -u jetty -nn | wc -l
$ ulimit -a
$ sudo vi /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535

同时发现了系统里也有Freddy Chu提到的"Too many CLOSE_WAIT"问题,谢谢Freddy Chu。

$ sudo vi /etc/sysctl.conf
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_intvl = 2
net.ipv4.tcp_keepalive_probes = 2
net.ipv4.tcp_keepalive_time = 1800

$ sudo sysctl -p

Update(2010-01-21): root用户要特殊设置

$ sudo vi /etc/security/limits.conf
root soft nofile 65535
root hard nofile 65535

解决Maven Jetty Plugin在Windows下的“文件锁定”问题

使用Maven进行Java的web开发,Jetty Plugin是必不可缺的插件,可以极大的提到开发效率。但在Windows环境下会遇到静态文件(html、css、js)被锁定、无法即时更新的问题。要想更新这些文件,只能先停掉Jetty,保存修改,再启动Jetty,非常不方便。
解决办法是这样的:
1、从jetty.jar中解出webdefault.xml(位于org.mortbay.jetty.webapp包下)这个文件,把这个useFileMappedBuffer参数设为false

useFileMappedBuffer true

2、把修改后的webdefault.xml文件跟pom.xml放在一起
3、修改pom.xml里的Jetty Plugin的配置,加入webdefault.xml

... org.mortbay.jetty
maven-jetty-plugin
6.1.7

/
webdefault.xml
...

...
...

Web页面自动打印PDF文件的实现

借助iText和Adobe Acrobat的Javascript支持,可以实现PDF文档的自动打印(可以做到不显示打印对话框)

......

PdfReader reader = new PdfReader(pdfStream); [1]

StringBuffer script = new StringBuffer(); [2]
script.append("this.print({bUI: false,bSilent: true,bShrinkToFit: false});")
.append("\r\nthis.closeDoc();");

ByteArrayOutputStream bos = new ByteArrayOutputStream(pdfStream.length);
try {
PdfStamper stamp = new PdfStamper(reader, bos); [3]
stamp.setViewerPreferences(PdfWriter.HideMenubar
| PdfWriter.HideToolbar | PdfWriter.HideWindowUI);
stamp.addJavaScript(script.toString());
stamp.close();
} catch (DocumentException e) {
logger.error(e.getMessage(), e.getCause());
}

return new StreamingResolution("application/pdf",
new BufferedInputStream(new ByteArrayInputStream(bos.toByteArray()))) ;

......

代码说明:
1、pdfStream是用iText生成的PDF文档字节流
2、script是要加入到PDF文档里的Javascript代码。这段Javascript代码先调用PDF文档对象的print方法,然后用closeDoc方法关闭文档。print方法的参数指定了不要显示打印对话框,参数含义参考Acrobat JavaScript Scripting Reference
3、bos用来保存加入Javascript后的PDF文档字节流,它作为PdfStamper对象的输出流。PdfStamper对象调用addJavaScript方法将Javascript代码加入到PDF文档中。

Web页面嵌入PDF文档: