使用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 <pid_of_jvm> 1000
# jmap -dump:format=b,file=HeapDump.hprof <pid_of_jvm>

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

Using Postgresql

1. create tablespace
$ mkdir -p /home/postgresql/data
$ sudo chown -R postres:postres /home/postgresql/data
$ sudo chmod -R og-rx /home/postgresql/data
$ sudo su – postgres
$ psql
postgres=# create tablespace newspace location ‘/home/postgresql/data’;

2. move a database to this new tablespace
use a php script from here to generate sql
$ sudo yum install php-pgsql
$ ./generate-mv-db.php
$ sudo su – postgres
$ psql -d mydb -f migrate_localhost_mydb_newspace.sql

3. install postgis
a. install proj4.7
$ sudo yum install proj
b. install geos 3.2.2
$ tar xvjf geos-3.2.2.tar.bz2
$ cd geos-3.2.2
$ ./configure –prefix=/usr
$ make && sudo make install
$ sudo ldconfig

must do this, otherwise postgis will fail to locate libgeos_c.so.1

c. install postgis-1.5.1.tar.gz
$ tar xvzf postgis-1.5.1.tar.gz
$ cd postgis-1.5.1
$ ./configure
$ make && sudo make install
d. create a spatially-enabled database
$ sudo su – postgres
$ createdb postgis_template -U postgres;
$ cd /usr/share/pgsql/contrib/postgis-1.5
$ createlang plpgsql postgis_template
$ psql -d postgis_template -f postgis.sql
$ psql -d postgis_template -f spatial_ref_sys.sql

再谈使用Commons HttpClient产生大量CLOSE_WAIT

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

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

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

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

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

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

再次遭遇大量CLOSE_WAIT

今天下午线上的Jetty服务又停止响应了。不过与上次不同的是,日志里没有Too many open files的问题(看来ulimit设置管用了),看不出任何问题。于是用netstat检查网络连接,发现了大量CLOSE_WAIT状态的连接,而且都是连接到同一个IP地址。联想到程序里有调用外部网站服务的部分,检查这个IP,正是其中的一个服务。检查代码,发现开发人员使用Commons HttpClient时出了问题,居然忘记关闭连接了(method.releaseConnection();)!!后果就是对方关闭了连接,连接不断地变成CLOSE_WAIT状态,直到耗尽所有的网络资源,没法再建立连接了。

从这个教训看出,项目的研发管理真是没有到位啊!快速反复的需求、几乎可以忽略的设计、缺少代码走查、没有单元测试,怎能保证上线前发现这样的问题!

maven-eclipse-plugin v2.6与aspectj

最近一段时间以来,项目中使用maven-eclipse-plugin的eclipse:eclipse目标时比较恼人。pom里已经加入了aspectj的依赖,但生成的.classpath里却没有相应的classpathentry。

今天google到了解决办法,有两个:

1. 强制使用上个版本的maven-eclipse-plugin

<groupId>org.apache.maven.plugins</groupId>
 
<artifactId>maven-eclipse-plugin</artifactId>
 
<version>2.5.1</version>
</plugin>

2. 设置ajdtVersion为none

<groupId>org.apache.maven.plugins</groupId>
 
<artifactId>maven-eclipse-plugin</artifactId>
 
<configuration>
    
<ajdtVersion>none</ajdtVersion>
 
</configuration>             
</plugin>