Uploaded image for project: 'Weld'
  1. Weld
  2. WELD-920

Memory leak through the creational context of an @AppScoped bean when injecting Instance<>

    • Icon: Bug Bug
    • Resolution: Won't Do
    • Icon: Major Major
    • None
    • 1.1.0.CR3, 1.1.1.Final
    • Scopes & Contexts
    • None

      Given a simple dependent-scoped bean: public class InstanceBean {}, and an application-scoped bean (see below) to which an instance of the dependent-scoped bean is injected, each time the get() method is called on the instance, even though it's not used, a reference to it stays in the creational context of the application scoped bean (http://screencast.com/t/XqjQ1GB7Wv3). That way after several requests, where each one calls the method, more and more memory is leaked (http://screencast.com/t/s1VBx49i).

      Attached is a simple web application demonstrating this. To reproduce, deploy to AS6, click the "leak" button several times, and analyze the heap dump e.g. in JProfiler.

      @ApplicationScoped
      @Named("test")
      public class AppScopedBean {
      private Instance<InstanceBean> instanceBeanInstance;

      @Inject
      public AppScopedBean(Instance<InstanceBean> instanceBeanInstance)

      { this.instanceBeanInstance = instanceBeanInstance; }

      public AppScopedBean() {
      }

      public void leakOneInstance()

      { System.out.println("Leaked!"); instanceBeanInstance.get(); }

      }

            [WELD-920] Memory leak through the creational context of an @AppScoped bean when injecting Instance<>

            Rejecting as this behavior is expected. An optimization has been made as part of WELD-1076 so that only dependencies that define a @PreDestroy callback are kept within CreationalContext. A new issue will be filed provided an explicit destruction mechanism is introduced in CDI 1.1

            Jozef Hartinger added a comment - Rejecting as this behavior is expected. An optimization has been made as part of WELD-1076 so that only dependencies that define a @PreDestroy callback are kept within CreationalContext. A new issue will be filed provided an explicit destruction mechanism is introduced in CDI 1.1

            This requires new API, as per CDI 1.1. discussion, hence moved to 2.x.

            Ales Justin added a comment - This requires new API, as per CDI 1.1. discussion, hence moved to 2.x.

            Now that I understand this a little better, I like the idea of analyzing the dependent instance graph and not tracking (managing) any instances that don't have @PreDestroy/@Disposer methods. It should be a fully transparent optimization.

            +1 on putting a big warning in the docs about Instance<>, as this has tripped up a bunch of people so far.

            Joshua Davis (Inactive) added a comment - Now that I understand this a little better, I like the idea of analyzing the dependent instance graph and not tracking (managing) any instances that don't have @PreDestroy/@Disposer methods. It should be a fully transparent optimization. +1 on putting a big warning in the docs about Instance<>, as this has tripped up a bunch of people so far.

            I suspected this might be the answer .

            Anyway, first maybe the use-case. I want to integrate with a third-party framework, where in a listener I need to invoke a method on a bean. As there's no standard static-injection (which would be ugly anyway), the "preferred" way as I understood is to pass an Instance<> to the listener's constructor, and then register that listener instance with the framework. This works well when the injected bean has e.g. request scope, but obviously doesn't work well if the bean is dependently-scoped (and also Weld doesn't have the stateless scope, which would I think be the proper scope for the bean I want to look up, as dependent here obviously is a very bad replacement).

            Also, at least at first sight, Instance<Something> shouldn't behave differently depending on where it is injected? Though I understand that if you want to discard the bean, you need to have it working as it is currently.

            I think that either solution (1) or (2) would be good, and certainly a big warning in the docs about how Instance<> works as it's really not obvious .

            Adam

            Adam Warski (Inactive) added a comment - I suspected this might be the answer . Anyway, first maybe the use-case. I want to integrate with a third-party framework, where in a listener I need to invoke a method on a bean. As there's no standard static-injection (which would be ugly anyway), the "preferred" way as I understood is to pass an Instance<> to the listener's constructor, and then register that listener instance with the framework. This works well when the injected bean has e.g. request scope, but obviously doesn't work well if the bean is dependently-scoped (and also Weld doesn't have the stateless scope, which would I think be the proper scope for the bean I want to look up, as dependent here obviously is a very bad replacement). Also, at least at first sight, Instance<Something> shouldn't behave differently depending on where it is injected? Though I understand that if you want to discard the bean, you need to have it working as it is currently. I think that either solution (1) or (2) would be good, and certainly a big warning in the docs about how Instance<> works as it's really not obvious . Adam

            No new methods on instance. (2) would be a config options. For the CDI improvement it would be some sort of explicit destroy() method on Instance I think.

            Pete Muir (Inactive) added a comment - No new methods on instance. (2) would be a config options. For the CDI improvement it would be some sort of explicit destroy() method on Instance I think.

            That explains it then, thanks! JIRA issue: CDI-139

            So in my case weld is handing me managed instances from the DependentContext. The context hangs on to these instances so that it can later call 'destroy' lifecycle methods on them when the context goes away, which it never does. Makes sense. However, it might be good to point out this difference a little more clearly in the Weld docos, as folks who are used to Guice and PicoContainer might be surprised by this.

            Idea #2 sounds simple. Either way, would that be a new method on Instance or something?

            Joshua Davis (Inactive) added a comment - That explains it then, thanks! JIRA issue: CDI-139 So in my case weld is handing me managed instances from the DependentContext . The context hangs on to these instances so that it can later call 'destroy' lifecycle methods on them when the context goes away, which it never does. Makes sense. However, it might be good to point out this difference a little more clearly in the Weld docos, as folks who are used to Guice and PicoContainer might be surprised by this. Idea #2 sounds simple. Either way, would that be a new method on Instance or something?

            It appears that the discussion we had about this issue has not made it to the issue :-/. Anyway, in essence the behavior you are seeing is expected in CDI 1.0, and this is not really an implementation bug. If you use Instance<> to create an instance of a bean, then that instance becomes dependent on the creator, and therefore you should not expect to see the instance discarded until the parent instance is discarded.

            We can describe instances which are attached (as the CDI 1.0 spec requires) as "managed" instances, and those which the user takes responsibility for cleaning up themselves as "unmanaged" instances. In CDI 1.1 I would like to add support for unmanaged instances (the impl will just hand these over and forget about them) and also to allow the app to request an unmanaged instance is cleaned up. Please can someone file a CDI issue for this?

            Weld could certainly be more friendly and more proactively discard instances. Ideas:

            1) Analyse the dependent instance graph, and if there are no @PreDestroy/@Disposer callbacks on in the graph, do not store the dependent instance for cleanup (this would be a good general optimization
            (2) Add a config option to allow instances created from Instance to be held only as long as the app holds a reference, and if the app doesn't hold a reference for it's lifetime, then Weld would not do any cleanup (Weld would hold a weak ref).

            Pete Muir (Inactive) added a comment - It appears that the discussion we had about this issue has not made it to the issue :-/. Anyway, in essence the behavior you are seeing is expected in CDI 1.0, and this is not really an implementation bug. If you use Instance<> to create an instance of a bean, then that instance becomes dependent on the creator, and therefore you should not expect to see the instance discarded until the parent instance is discarded. We can describe instances which are attached (as the CDI 1.0 spec requires) as "managed" instances, and those which the user takes responsibility for cleaning up themselves as "unmanaged" instances. In CDI 1.1 I would like to add support for unmanaged instances (the impl will just hand these over and forget about them) and also to allow the app to request an unmanaged instance is cleaned up. Please can someone file a CDI issue for this? Weld could certainly be more friendly and more proactively discard instances. Ideas: 1) Analyse the dependent instance graph, and if there are no @PreDestroy/@Disposer callbacks on in the graph, do not store the dependent instance for cleanup (this would be a good general optimization (2) Add a config option to allow instances created from Instance to be held only as long as the app holds a reference, and if the app doesn't hold a reference for it's lifetime, then Weld would not do any cleanup (Weld would hold a weak ref).

            Joshua Davis (Inactive) added a comment - - edited

            In my case, it looks like InstanceImpl->creationalContext->dependentInstances keeps growing, as does parentDependentInstances

            Joshua Davis (Inactive) added a comment - - edited In my case, it looks like InstanceImpl->creationalContext->dependentInstances keeps growing, as does parentDependentInstances

            Joshua Davis (Inactive) added a comment - - edited

            I think this is also happening with weld-se 1.1.1.Final, for example:

            WeldContainer weld = new Weld().initialize();
            weld.event().select(ContainerInitialized.class).fire(new ContainerInitialized());
            Instance<TestBean> instance = weld.instance().select(TestBean.class);
            TestBean bean = null;
            for (int i = 0 ; i < 100000; i++) {
                bean = instance.get();
            }
            

            See http://sfwk.org/Community/OOMEWithAWeldSEObjectCreationBenchmark

            Joshua Davis (Inactive) added a comment - - edited I think this is also happening with weld-se 1.1.1.Final, for example: WeldContainer weld = new Weld().initialize(); weld.event().select(ContainerInitialized.class).fire( new ContainerInitialized()); Instance<TestBean> instance = weld.instance().select(TestBean.class); TestBean bean = null ; for ( int i = 0 ; i < 100000; i++) { bean = instance.get(); } See http://sfwk.org/Community/OOMEWithAWeldSEObjectCreationBenchmark

            Forgot to mention that I tested on AS6 Final and AS6 Final updated with Weld 1.1.1.Final.

            Adam Warski (Inactive) added a comment - Forgot to mention that I tested on AS6 Final and AS6 Final updated with Weld 1.1.1.Final.

              rhn-engineering-jharting Jozef Hartinger
              adamw_jira Adam Warski (Inactive)
              Votes:
              3 Vote for this issue
              Watchers:
              8 Start watching this issue

                Created:
                Updated:
                Resolved: