最近遇到一个WebService内存泄露的问题。使用jconsole连接后发现非堆内存和loaded classes在每次执行full GC后都会增长。classes增长的非常有规律,每次增加5000个左右。
因为CXF中使用了JAXB,怀疑是JAXB被多次实例化造成的。然后使用MAT,查找Duplicate Classes,不过MAT显示没有Duplicate Classes。
没办法,改用JVM参数追踪
* -XX:+TraceClassLoading
* -XX:+TraceClassUnloading
在trace日志中确认就是WebService相关的那些classes被多次加载了
剩下的问题就是调查哪里的JAXB被重复实例化了。跟踪发现CXF使用一个CacheMap来存储构造过的WebService类。
private static final Map<Set<Class<?>>, CachedContextAndSchemas> JAXBCONTEXT_CACHE
= new CacheMap<Set<Class<?>>, CachedContextAndSchemas>();
........
synchronized (JAXBCONTEXT_CACHE) {
cachedContextAndSchemas = JAXBCONTEXT_CACHE.get(classes);
}
if (cachedContextAndSchemas == null) {
JAXBContext ctx = JAXBContext.newInstance(classes.toArray(new Class[classes.size()]), map);
cachedContextAndSchemas = new CachedContextAndSchemas(ctx);
synchronized (JAXBCONTEXT_CACHE) {
JAXBCONTEXT_CACHE.put(classes, cachedContextAndSchemas);
}
}
CacheMap中的key是weak references,value是strong references。所以GC时候会回收对应的key,但是schema仍存留在内存中。一旦在cache中没有发现key,就会重新实例化!!
解决方法也很简单,JAXBCONTEXT_CACHE使用HashMap就可以了。