Spring Framework
  1. Spring Framework
  2. SPR-7385

org.aspectj.weaver.World.reset() not called on Context-Shutdown: Memory-Leak caused

    Details

    • Type: Bug Bug
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 2.5.6
    • Fix Version/s: None
    • Component/s: Core:AOP
    • Labels:
      None
    • Last commented by a User:
      false

      Description

      When using Spring together with AspektJ a memory leak occurs. This memory leak prevents the application context and associated instances from beeing cleaned up. Root of this memory leak is the class org.aspectj.weaver.ResolvedType which contains public static final fields which will be populated with an (runtime) instance of class org.aspectj.weaver.World / org.aspectj.weaver.reflect.ReflectionWorld. Following the reference tree the ReflectionWorld instance has a reference to an org.springframework.aop.aspectj.AspectJExpressionPointcut$BeanNamePointcutDeignatorHandler instance which inturn has a reference path to the ApplicationContext. This reference to the ApplicationContext ( a XmlWebApplicationContext in our case ) "protects" the context and some application beans (especially ApplicationListeners) from beeing cleaned up by the GC. Thus continious re-deploment the web application causes an OutOfMemoryError after a while.

      There are two solutions that come into my mind:

      1. Calling org.aspectj.weaver.World.reset() on application context shutdown/close. This method clears all World/ReflectionWorld instances in ResolvedType static final memebers thus making them eligible for garbage collection. This will be the option i'll try. I plan to create a Bean as an ApplicationListener which creates and starts a new thread if it receives a ContextClosedEvent. This new thread waits until context.isActive() == false and calls World.reset() than. Afterwards the Thread will end so that it can be cleaned up itself. The risk/bad thing with that option is, if another ApplicationContext exists in the same VM/Classloader using AspectJ this process could clear their static world as well. I really don't know if that is the case / if that really happens. In our case using Tomcat the webapp will be loaded in it's own Classloader so the application context only clears the aspectj world of it's own classloader and does not affect other applications deployen at the same tomcat.

      2. From a spring perspektive the best thing could be removing the org.springframework.aop.aspectj.AspectJExpressionPointcut$BeanNamePointcutDeignatorHandler from the pointcutDesignators set of org.aspectj.weaver.reflect.ReflectionWorld thus enabling the cleanup of the application context with the overhead of the remaining AspectJ instances.

        Activity

        Hide
        Jon Seymour added a comment -

        I have now realised SPR-7677 is actually a non-issue, so my note about the linkage to SPR-7677 is not relevant.

        The caveat on the suggested workaround above is that until SPR-7385 issue is addressed, it is important that there is a bound on the number of Spring contexts created in the scope of single classloader. If there is no bound, the problem documented here will lead to a leak irrespective of whether AspectJ is loaded in the same classloader or not.

        Show
        Jon Seymour added a comment - I have now realised SPR-7677 is actually a non-issue, so my note about the linkage to SPR-7677 is not relevant. The caveat on the suggested workaround above is that until SPR-7385 issue is addressed, it is important that there is a bound on the number of Spring contexts created in the scope of single classloader. If there is no bound, the problem documented here will lead to a leak irrespective of whether AspectJ is loaded in the same classloader or not.
        Hide
        Jon Seymour added a comment -

        Just in case this helps others.

        We have been suffering a resource leak of this kind because we were seeing multiple Spring bean factory lifecycles within the scope of a single application classloader scope. This was happening because we had an EAR containing Spring EJBs that did not contain a Web module. As such, when all the EJBs were purged from the pool, the application context was been cleaned up, but the resources pinned by AspectJ were not. These pinned resources would accumulate overtime as the EJBs were recreated and then destroyed.

        So a workaround that would help in our case is to install a web module with a reference to the bean factory group that contains the EJBs bean factory so that even if the EJBs disappear, the bean factory group does not. This will ensure that only create the Spring bean factory once in the lifecycle of the application classloader and so do not suffer the slow leak.

        Show
        Jon Seymour added a comment - Just in case this helps others. We have been suffering a resource leak of this kind because we were seeing multiple Spring bean factory lifecycles within the scope of a single application classloader scope. This was happening because we had an EAR containing Spring EJBs that did not contain a Web module. As such, when all the EJBs were purged from the pool, the application context was been cleaned up, but the resources pinned by AspectJ were not. These pinned resources would accumulate overtime as the EJBs were recreated and then destroyed. So a workaround that would help in our case is to install a web module with a reference to the bean factory group that contains the EJBs bean factory so that even if the EJBs disappear, the bean factory group does not. This will ensure that only create the Spring bean factory once in the lifecycle of the application classloader and so do not suffer the slow leak.
        Hide
        Jon Seymour added a comment -

        Here's a link to a bug report that I have filed with AspectJ that describes a scenario that can result in this issue unexpectedly pinning a large amount of memory.

        https://bugs.eclipse.org/bugs/show_bug.cgi?id=328558

        Show
        Jon Seymour added a comment - Here's a link to a bug report that I have filed with AspectJ that describes a scenario that can result in this issue unexpectedly pinning a large amount of memory. https://bugs.eclipse.org/bugs/show_bug.cgi?id=328558
        Hide
        Cédric Champeau added a comment -

        Any update on this ? I am facing a similar problem in an application which periodically creates application contexts. Those contexts inherit a parent one, so the solution of cleaning up the world doesn't work.

        Seen on Spring 3.0.3.

        Show
        Cédric Champeau added a comment - Any update on this ? I am facing a similar problem in an application which periodically creates application contexts. Those contexts inherit a parent one, so the solution of cleaning up the world doesn't work. Seen on Spring 3.0.3.
        Hide
        Chris Beams added a comment -

        Andy, please triage as time allows. De-scheduling from 3.1 RC1 until that is complete.

        Show
        Chris Beams added a comment - Andy, please triage as time allows. De-scheduling from 3.1 RC1 until that is complete.

          People

          • Assignee:
            Andy Clement
            Reporter:
            Stefan Penndorf
            Last updater:
            Trevor Marshall
          • Votes:
            5 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

            • Created:
              Updated:
              Days since last comment:
              2 years, 43 weeks, 1 day ago