CDI | 应用/依赖作用域 | 内存泄漏:javax.enterprise.inject.Instance<T>无法被垃圾回收
使用
javax.enterprise.inject.Instance<T>导致TomEE应用内存泄漏的问题分析与解决 嘿,最近在TomEE的Java应用里用javax.enterprise.inject.Instance<T>做懒加载和动态注入的时候,居然踩了个内存泄漏的坑!这还是我第一次碰到这种情况,后来翻Java EE的库文档才发现,人家早就在接口注释里给过警告了!
package javax.enterprise.inject;
public interface Instanceextends Iterable , Provider {
/**
* 销毁给定的上下文实例。
* 该方法专为{@link javax.enterprise.context.Dependent}作用域设计……
*/
}
问题到底出在哪?
- 核心问题出在Dependent作用域的Bean身上:当你用
Instance<T>动态获取这类Bean时,容器不会自动帮你管理它的生命周期。毕竟Dependent作用域的Bean默认绑定到注入点的生命周期,但Instance是动态获取的,容器根本没法追踪你什么时候用完这个Bean。 - 如果用完之后不手动调用
destroy()方法销毁实例,这些Bean就会一直占着内存,时间一长就会引发内存泄漏。
怎么解决这个问题?
- 必须手动销毁Dependent作用域的Bean:每次通过
Instance.get()拿到Bean实例后,一定要在使用完毕后(最好用try-finally块保证执行)调用Instance.destroy(bean)来释放资源。举个实际代码例子:
@Inject Instance<MyDependentScopedBean> beanInstance; public void processBusiness() { MyDependentScopedBean bean = beanInstance.get(); try { // 这里执行Bean的业务逻辑操作 } finally { beanInstance.destroy(bean); // 关键一步:手动销毁实例 } }
- 要是你获取的是RequestScoped、Singleton这类非Dependent作用域的Bean,容器会自动管理它们的生命周期,不用手动调用destroy,但也别长期持有
Instance的引用,避免不必要的内存占用。 - 排查内存泄漏的时候,可以用VisualVM或者MAT这类工具分析堆内存,看看是不是有大量未销毁的Dependent Bean实例,就能确认是不是这个原因导致的了。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

