如何通过BeanManager创建和销毁CDI(Weld)托管Bean?

Ben*_*rby 7 java garbage-collection cdi managed-bean jboss-weld

我正在尝试使用BeanManager而不是Instance .select().get()创建CDI托管bean的实例.

这被建议作为我已经使用ApplicationScoped bean和他们的家属的垃圾收集的问题的解决方法 - 请参阅CDI应用程序和依赖范围可以合谋影响垃圾收集?对于背景和建议的解决方法.

如果在ApplicationScoped bean上使用Instance编程查找方法,Instance对象和从中获取的任何bean最终都依赖于ApplicationScoped bean,因此共享它的生命周期.但是,如果使用BeanManager创建bean,则会在Bean实例本身上设置句柄,并且显然可以明确地销毁它,我理解这意味着它将被GCed.

我目前的方法是在BeanManagerUtil类中创建bean,并返回Bean,实例和CreationalContext的复合对象:

public class BeanManagerUtil {

    @Inject private BeanManager beanManager;

    @SuppressWarnings("unchecked")
    public <T> DestructibleBeanInstance<T> getDestructibleBeanInstance(final Class<T> type,
            final Annotation... qualifiers) {

        DestructibleBeanInstance<T> result = null;
        Bean<T> bean = (Bean<T>) beanManager.resolve(beanManager.getBeans(type, qualifiers));
        if (bean != null) {
            CreationalContext<T> creationalContext = beanManager.createCreationalContext(bean);
            if (creationalContext != null) {
                T instance = bean.create(creationalContext);
                result = new DestructibleBeanInstance<T>(instance, bean, creationalContext);
            }
        }
        return result;
    }
}

public class DestructibleBeanInstance<T> {

    private T instance;
    private Bean<T> bean;
    private CreationalContext<T> context;

    public DestructibleBeanInstance(T instance, Bean<T> bean, CreationalContext<T> context) {
        this.instance = instance;
        this.bean = bean;
        this.context = context;
    }

    public T getInstance() {
        return instance;
    }    

    public void destroy() {
        bean.destroy(instance, context);
    }
}
Run Code Online (Sandbox Code Playgroud)

从这里,在调用代码中,我可以获得实际的实例,将其放在地图中供以后检索,并正常使用:

private Map<Worker, DestructibleBeanInstance<Worker>> beansByTheirWorkers =
    new HashMap<Worker, DestructibleBeanInstance<Worker>>();
...
DestructibleBeanInstance<Worker> destructible =
        beanUtils.getDestructibleBeanInstance(Worker.class, workerBindingQualifier);
Worker worker = destructible.getInstance();
...
Run Code Online (Sandbox Code Playgroud)

当我完成它时,我可以查找可破坏的包装器并在其上调用destroy(),并且应该清理bean及其依赖项:

DestructibleBeanInstance<JamWorker> workerBean =
        beansByTheirWorkers.remove(worker);
workerBean.destroy();
worker = null;
Run Code Online (Sandbox Code Playgroud)

然而,在运行几个工作人员并离开我的JBoss(7.1.0.Alpha1-SNAPSHOT)20分钟左右后,我可以看到GC发生

2011.002: [GC
Desired survivor size 15794176 bytes, new threshold 1 (max 15)
1884205K->1568621K(3128704K), 0.0091281 secs]
Run Code Online (Sandbox Code Playgroud)

然而,JMAP直方图仍然显示旧工人及其依赖的实例,unGCed.我错过了什么?

通过调试,我可以看到创建的bean的上下文字段具有正确的Worker类型的上下文,没有incompleteInstances,也没有parentDependentInstances.它有许多dependentInstances,它们与worker上的字段一样.

Worker上的其中一个字段实际上是一个实例,当我将此字段与通过编程实例查找检索到的Worker字段进行比较时,它们的CreationalContext组成略有不同.通过Instance查找的Worker上的Instance字段将worker本身置于incompleteInstances下,而从BeanManager中检索的Worker上的Instance字段则没有.它们都具有相同的parentDependentInstances和dependentInstances.

这告诉我,我没有正确反映实例的检索.这会导致缺乏破坏吗?

最后,在调试时,我可以看到在我的DestructibleBeanInstance.destroy()中调用了bean.destroy(),这将进入ManagedBean.destroy,我可以看到依赖对象被作为.release()的一部分被销毁.但是他们仍然没有收集垃圾!

任何有关这方面的帮助将非常感谢!谢谢.

Lig*_*ard 4

我会更改您粘贴的代码中的一些内容。

  1. 使该类成为常规 java 类,无需注入并传入 BeanManager。那样的话可能会出现一些混乱。这不太可能,但有可能。
  2. 使用它创建一个新的 CreationalContext 本质上BeanManager.createCreationContext(null)将为您提供一个依赖范围,您可以在完成调用后释放该范围CreationalContext.release()

您可以通过在 中已有的 CreationalContext 上调用 release 方法,让一切按照您想要的方式正常工作DestructibleBeanInstance,假设该 CreationalContext 中没有其他内容Beans会扰乱您的应用程序。首先尝试一下,看看它是否会把事情搞砸。