我们开始尝试使用CDI实现我们的后端服务。场景是这样的:
带有@Startup的EJB在部署EAR时启动。一个ApplicationScoped bean被注入到:
@ApplicationScoped
public class JobPlatform {
private PooledExecutor threadHolder;
@Inject @Any
private Instance<Worker> workerSource;
...
该bean还有一个Observer方法,当观察到一个事件时,该方法会从Instance workerSource获取一个工作bean,并将其放在threadPool中,最终运行到完成。
一切正常。然而……我们已经开始看到垃圾收集问题。JMAP堆直方图显示,有许多这样的工作线程挂在周围,未被垃圾收集。
我们认为这是由于CDI范围的组合。@Dependant的API页面(http://docs.jboss.org/cdi/api/1.0-SP1/javax/enterprise/context/Dependent.html)更清楚地强化了文档中的内容:
- 将作用域为@Dependent的bean实例注入到字段、bean构造函数或初始化方法中,是该bean或Java EE组件类实例的依赖对象。
- 将作用域为@Dependent的bean实例注入到一个生产者方法中,它是正在被生产的生产者方法bean实例的依赖对象。
- 通过直接调用instance获得作用域为@Dependent的bean实例是instance的实例的依赖对象。
那么,接下来是:
- workerSource bean绑定到JobPlatform,因此有一个applicationscope的生命周期
- 使用该实例检索的任何工作bean都被绑定到它,因此具有applicationscope生存期
- 因为ApplicationScoped上下文的beanstore(我对术语的了解在这里有点模糊)仍然有对工作bean的引用,所以它们没有被销毁/垃圾收集
使用CDI的人都同意吗?您是否遇到过这种缺乏垃圾收集的情况?如果有,您能提出一些解决方法吗?
工作者不能是ApplicationScoped的,但是平台必须是。如果我们要创建一个自定义的WorkerScope,并用它注释每个worker类,这是否足以分离worker和实例源之间的依赖关系?
也有一些建议是否有可能破坏CDI作用域?
希望你能帮忙,谢谢。
你的理解是正确的。这是规范中的一个疏忽,将在CDI 1.1中修复。Instance
在SessionScoped
或ApplicationScoped
等长时间运行的作用域中使用时可能会出现内存泄漏,正如您所描述的那样。您需要做的是获得实例的Contextual
或Bean
,并以这种方式销毁它。
对于你正在做的事情,为了避免内存泄漏,你最好使用BeanManager方法来创建实例(这样你也会有一个对Bean
的处理,可以销毁它)而不是Instance
。
在研究如何实现Jason建议的解决方案时,我发现了与此问题相关的更多资源:
问题:https://issues.jboss.org/browse/CDI-139和https://issues.jboss.org/browse/WELD-920
beanManager操作示例:https://issues.jboss.org/browse/CDI-14?focusedCommentId=12601344&页面= com.atlassian.jira.plugin.system.issuetabpanels: comment-tabpanel #评论- 12601344
或org.jboss.seam.faces.util.BeanManagerUtils