Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java config FactoryBean proxy causes class loader leak [SPR-9274] #13912

Closed
spring-projects-issues opened this issue Mar 28, 2012 · 3 comments
Closed
Assignees
Labels
in: core status: backported type: bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Mar 28, 2012

Harald Wellmann opened SPR-9274 and commented

Redeploying my web application in Tomcat a couple of times in a row, I had a PermGen space OutOfMemoryError. A heap dump revealed the cause to be a net.sf.cglib.proxy.Callback stored in thread-local memory, so that the associated class loader could not get garbage collected.

The callback holds a reference to ConfigurationClassEnhancer.GetObjectMethodInterceptor, which brought me closer to the root cause of the problem.

After changing my Spring Configuration from

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new JpaTransactionManager(myEntityManagerFactory().getObject());
    }

    @Bean
    public AbstractEntityManagerFactoryBean myEntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
        bean.setDataSource(myDataSource);
        bean.setPersistenceProvider(new HibernatePersistence());
        bean.setPersistenceUnitName("myPU");
        return bean;
    }

to

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new JpaTransactionManager(myEntityManagerFactory());
    }

    @Bean
    public EntityManagerFactory tcmEntityManagerFactory() {
        LocalContainerEntityManagerFactoryBean bean = new LocalContainerEntityManagerFactoryBean();
        bean.setDataSource(myDataSource);
        bean.setPersistenceProvider(new HibernatePersistence());
        bean.setPersistenceUnitName("myPU");
        bean.afterPropertiesSet();
        return bean.getObject();
    }

I can redeploy my application without memory leaks.

I suspect the problem would disappear if ConfigurationClassEnhancer.enhanceFactoryBean() called Enhancer.registerStaticCallbacks() instead of Enhancer.registerCallbacks() (see #10601).


Affects: 3.1 GA, 3.2.8

Issue Links:

  • #14941 Concurrent creation of the same Configuration class in different contexts is not thread-safe
  • #10601 Singleton-scoped @Bean methods behave like prototypes in a Spring DM environment

Backported to: 3.2.9

5 votes, 5 watchers

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jun 5, 2012

Peter Ertl commented

I can confirm this memory issue and it's pretty serious imho. It actually wasted so much memory on our integration system (> 1GB) that in the end the tests died with OOME, taking forever since GC was using up most of time (> 1h30m instead of 16min). I would upgrade the severity to at least 'serious' since having this bug means that people upgrading to javaconfig will have tests that do not run in a real-life application.

I created a local copy of class org.springframework.context.annotation.ConfigurationClassEnhancer and replaced
Enhancer.registerCallbacks() with Enhancer.registerStaticCallbacks() as Harald suggested. That fixed the issue here.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Jun 5, 2012

Peter Ertl commented

I would also suggest to add a call to Enhancer.registerCallback(obj.getClass(), null) in DisposableBeanMethodInterceptor to clean up thread local callbacks like that:

  private static class DisposableBeanMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(final Object obj, final Method method, final Object[] args, final MethodProxy proxy) throws Throwable {
      Enhancer.registerStaticCallbacks(obj.getClass(), null);
      >>>>  Enhancer.registerCallbacks(obj.getClass(), null);

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Apr 28, 2014

Juergen Hoeller commented

The underlying problem has been addressed in the 4.0 line through a wider-scale revision of configuration class processing (#14941).

That said, since that registerCallbacks call in enhanceFactoryBean is still there in the 3.2.x line, I'll locally fix it there as well - for the 3.2.9 release.

Sorry that it took so long... This issue unfortunately wasn't ever noticed.

Juergen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core status: backported type: bug
Projects
None yet
Development

No branches or pull requests

2 participants