Как создать и уничтожить управляемые компоненты CDI (Weld) через BeanManager?

Я пытаюсь создать экземпляры управляемых компонентов CDI, используя BeanManager, а не экземпляр .выбирать.)(получить.)(

Это было предложено в качестве обходного пути к проблеме, которую я имел с ApplicationScoped бобами и сборкой мусора их иждивенцев-см. приложение CDI и зависимые области могут вступать в сговор, чтобы повлиять на сборку мусора? для фона и этого предлагаемого обходного пути.

если вы используете метод программного поиска экземпляра на компонент ApplicationScoped, объект экземпляра и любые компоненты, которые вы получаете от него, в конечном счете зависят от компонента ApplicationScoped и поэтому разделяют его жизненный цикл. Однако, если вы создаете бобы с помощью BeanManager, у вас есть дескриптор самого экземпляра Bean и, по-видимому, вы можете явно уничтожить его, что, как я понимаю, означает, что он будет GCed.

мой текущий подход заключается в создании компонента в классе BeanManagerUtil и возврате составного объекта 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);
    }
}

из этого в вызывающем коде я могу получить фактический экземпляр, поместить его в карту для последующего извлечения и использовать как обычно:

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

когда я закончу с ним, я могу найти разрушаемую оболочку и вызвать destroy () на ней, а Боб и его иждивенцы должны быть очищены:

DestructibleBeanInstance<JamWorker> workerBean =
        beansByTheirWorkers.remove(worker);
workerBean.destroy();
worker = null;

однако, после запуска нескольких рабочих и оставления моего JBoss (7.1.0.Alpha1-снимок) в течение 20 минут или около того, я вижу GC происходящее

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

однако гистограмма JMAP все еще показывает старых рабочих и их зависимые экземпляры, висящие вокруг, unGCed. Что я упускаю?

через отладку я вижу, что контекстное поле созданного компонента имеет контекстуальный правильный тип Worker, без incompleteInstances и без parentDependentInstances. Он имеет ряд dependentInstances,которые, как и ожидалось от полей на рабочем.

одно из этих полей на работника на самом деле экземпляр, и когда я сравниваю это поле с полем рабочего, полученным с помощью поиска программного экземпляра, они имеют немного другой состав CreationalContext. В поле instance на рабочий посмотрел через экземпляр был сам работник под incompleteInstances, в то время как поле экземпляра на работника, полученных из BeanManager не. Они оба имеют одинаковые parentDependentInstances и dependentInstances.

Это говорит мне о том, что я не отражал правильный поиск экземпляра. Может ли это способствовать отсутствию разрушений?

наконец, при отладке я вижу bean.destroy () вызывается в моем DestructibleBeanInstance.destroy (), и это проходит через ManagedBean.уничтожьте, и я могу видеть, что зависимые объекты уничтожаются как часть .выпускать.)( Однако они все еще не собирают мусор!

любая помощь будет очень ценится! Спасибо.

3 ответов


Я бы изменил пару вещей в коде, который вы вставили.

  1. сделайте этот класс обычным классом java, без инъекции и передачи в BeanManager. Что-то может быть не так. Это маловероятно, но возможно.
  2. создайте новый CreationalContext с помощью BeanManager.createCreationContext(null) Что даст вам по существу зависимую область, которую вы можете освободить, когда закончите, вызвав CreationalContext.release().

вы можете получить все, чтобы работать правильно как вы хотите, вызывая метод release в CreationalContext, который у вас уже есть в DestructibleBeanInstance, предполагая, что нет другого Beans В этом CreationalContext, который испортит ваше приложение. Попробуй сначала это и увидишь, если это все испортит.


передача в null должна выполняться только при введении некоторого класса, отличного от bean. В вашем случае вы вводите Боб. Однако я все равно ожидаю, что GC будет работать в этом случае, поэтому не могли бы вы подать JIRA в трекер проблем сварки с тестовым случаем и шагами для воспроизведения?


более подходящий способ решения вашей проблемы может быть использование динамического прокси для обработки разрушение зерен. Код для получения экземпляра класса bean programaticaly будет:

public static <B> B getBeanClassInstance(BeanManager beanManager, Class<B> beanType, Annotation... qualifiers) {
    final B result;
    Set<Bean<?>> beans = beanManager.getBeans(beanType, qualifiers);
    if (beans.isEmpty())
        result = null;
    else {
        final Bean<B> bean = (Bean<B>) beanManager.resolve(beans);
        if (bean == null)
            result = null;
        else {
            final CreationalContext<B> cc = beanManager.createCreationalContext(bean);
            final B reference = (B) beanManager.getReference(bean, beanType, cc);
            Class<? extends Annotation> scope = bean.getScope();
            if (scope.equals(Dependent.class)) {
                if (beanType.isInterface()) {
                    result = (B) Proxy.newProxyInstance(bean.getBeanClass().getClassLoader(), new Class<?>[] { beanType,
                            Finalizable.class }, new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            if (method.getName().equals("finalize")) {
                                bean.destroy(reference, cc);
                            }
                            try {
                                return method.invoke(reference, args);
                            } catch (InvocationTargetException e) {
                                throw e.getCause();
                            }
                        }
                    });
                } else
                    throw new IllegalArgumentException("If the resolved bean is dependent scoped then the received beanType should be an interface in order to manage the destruction of the created dependent bean class instance.");
            } else
                result = reference;
        }
    }
    return result;
}

interface Finalizable {
    void finalize() throws Throwable;
}

таким образом, пользовательский код проще. Он не должен заботиться о разрушении. Ограничение этого приложения заключается в том, что случай, когда полученный beanType не является интерфейсом, а разрешенный класс bean -@Dependent Не поддерживается. Но легко работать вокруг. Просто используйте интерфейс. Я проверил этот код (с JBoss 7.1.1), и он работает также для зависимых компонентов сеанса с состоянием.