使用Spring WebServiceTemplate与JAXB出现Finalizer内存泄漏如何解决
解决方案
1. 复用Unmarshaller实例,避免频繁创建新对象
默认Jaxb2Marshaller每次请求都会新建Unmarshaller实例,而Unmarshaller是非线程安全的,可以通过ThreadLocal缓存每个线程的实例,大幅减少对象创建销毁次数:
public class PooledJaxb2Marshaller extends Jaxb2Marshaller { private final ThreadLocal<Unmarshaller> unmarshallerThreadLocal = ThreadLocal.withInitial(() -> { try { return super.createUnmarshaller(); } catch (JAXBException e) { throw new RuntimeException("创建Unmarshaller失败", e); } }); @Override public Object unmarshal(Source source) throws XmlMappingException { Unmarshaller unmarshaller = unmarshallerThreadLocal.get(); try { return unmarshaller.unmarshal(source); } catch (JAXBException e) { throw convertJaxbException(e); } } }
修改WebServiceConfig中的marshaller Bean定义,使用自定义的PooledJaxb2Marshaller即可。
2. 替换HTTP消息发送器,消除HttpsURLConnectionImpl实例堆积
默认的HttpsUrlConnectionMessageSender基于JDK原生HttpURLConnection实现,连接实例销毁时会触发finalize方法。可以改用池化的Apache HttpComponents实现,从根源减少连接实例的创建:
首先新增依赖:
// 请根据你的Spring Boot版本选择匹配的版本号 implementation 'org.springframework.ws:spring-ws-support:2.4.7.RELEASE' implementation 'org.apache.httpcomponents:httpclient:4.5.14'
修改消息发送器配置:
@Bean public HttpComponentsMessageSender httpComponentsMessageSender(Props props, AllTimeouts allTimeouts) { // 配置连接池 PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); connectionManager.setMaxTotal(20); // 按实际并发量调整 connectionManager.setDefaultMaxPerRoute(10); // 配置基础认证 CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(props.getUsername(), props.getPassword())); // 构建HttpClient HttpClient httpClient = HttpClientBuilder.create() .setConnectionManager(connectionManager) .setDefaultCredentialsProvider(credentialsProvider) .setDefaultSocketTimeout(allTimeouts.getReadTimeout()) .build(); return new HttpComponentsMessageSender(httpClient); }
最后修改webServiceTemplate的messageSenders配置,替换为上述自定义的httpComponentsMessageSender即可。
3. 升级JAXB运行时版本,移除finalize逻辑
你当前使用的2.3.0版本的jaxb-runtime中,UnmarshallerImpl实现了finalize方法,2.3.2及以上版本已经移除了该方法,直接升级依赖即可解决该类的Finalizer队列堆积问题:
implementation 'javax.xml.bind:jaxb-api:2.3.1' implementation 'org.glassfish.jaxb:jaxb-runtime:2.3.6' // 可移除原来单独引入的activation依赖,新版jaxb-runtime已内置
4. 应急JVM调优方案(仅临时使用,不推荐长期生效)
如果暂时无法修改代码,可以通过JVM参数加快Finalizer队列处理速度:
- 调高Finalizer线程优先级:
-XX:FinalizerPriority=10 - 改用低延迟垃圾回收器(如ZGC/Shenandoah),提升Finalizer队列处理效率
验证方式
修改完成后执行压测,通过jmap -histo:live <pid>查看Finalizer对象数量,或导出堆dump确认是否还有大量UnmarshallerImpl、HttpsURLConnectionImpl实例堆积。
内容的提问来源于stack exchange,提问作者SorryForAsking
相关产品推荐
相关产品推荐

