-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 
Author Message
 Post subject: Hibernate webapp not garbage collected after it is stopped
PostPosted: Thu Sep 23, 2004 10:42 am 
Beginner
Beginner

Joined: Tue Sep 02, 2003 9:28 pm
Posts: 25
When using the tomcat manager app to stop a web app, tomcat discards the classloader for the web app context so that the memory used by the web app can get garbage collected. When the web app uses Hibernate, however, the classloader does not get garbage collected. This means that all classes that it loaded and any static references that these classes may have do not get garbage collected either. This results in memory leakage everytime the web app is stopped with the tomcat manager app.

I've created a simple web app that demonstrates the problem. Note that no Hibernate session or SessionFactory is ever created - it is only necessary to configure hibernate to get the problem to occur.

Prior to narrowing it down to the hibernate configuration process, the code also created a SessionFactory object. When this object is created, cglib is loaded and I did find one static reference to the classloader with the help of a profiler in net.sf.cglib.core.ReflectUtils. I recompiled cglib with a cleanUp() method to null out this reference and called this method from the servlet destroy() method, but the memory leak still occurred. This led to removing the call to buildSessionFactory(). Now with creating the configuration object only, the memory leak will occur. I can't determine what is still pinning the classloader in memory.

If I remove the hibernate code, the classloader is garbage collected properly.

Any insight is appreciated.

-Jay


Hibernate version:
2.1.6

Tomcat version:
4.1.30

hibernate.cfg.xml
Code:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">

<hibernate-configuration>
   <session-factory >
      <!-- mapping files -->
      <mapping resource="z/Foo.xml"/>
   </session-factory>
</hibernate-configuration>


Mapping documents:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
      "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping >
  <class name="z.Foo" table="FOO">
    <id name="id" column="ID" type="long" unsaved-value="null">
      <generator class="net.sf.hibernate.id.Assigned"/>
    </id>
  </class>
</hibernate-mapping>


Code in doGet of Servlet:
Code:
      SessionFactory sf = null;
      try {
         Configuration cfg = new Configuration().configure();
         // if a SessionFactory is created, cglib.ReflectUtils holds on to the classloader
         //sf =  new Configuration().configure().buildSessionFactory();
      }
      catch (HibernateException e) {
         throw new ServletException(e);
      }
      finally {
         if (sf != null) {
            try {
               sf.close();
            }
            catch (HibernateException e) {
               throw new ServletException(e);
            }
         }
      }

      PrintWriter out = httpServletResponse.getWriter();
      out.write("<html><head></head><body>" +
            "Hello World " + new Date() +
            "</body></html>");
      out.flush();


Code in destroy of Servlet:
Code:

      // this is needed since Log4J uses java.beans package for configuration and since the classloader is about to be discarded by tomcat
      // see the javadoc for java.beans.Introspector for more details
     
      Introspector.flushCaches();



Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 23, 2004 11:26 am 
Newbie

Joined: Wed Jul 28, 2004 3:20 pm
Posts: 16
I believe this is a known issue. Spring was having the same problem with CGLIB holding onto static references. They fixed it in their last release (spring did, not the cglib folks) but I haven't heard if it's been fixed here yet.

The good thing is that it's only a bug in the case you describe, hot re-deploying webapps. In production it shouldn't be a problem (unless you try to re-deploy on a live site).


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 23, 2004 12:06 pm 
Beginner
Beginner

Joined: Tue Sep 02, 2003 9:28 pm
Posts: 25
I was thinking it was cglib too, but when I changed the test code to just create the hibernate configuration object and nothing else, the memory leak still occurs (and cglib isn't even loaded in this scenario).

While there are references to the classloader in cglib that cause the leak as well (ReflectUtils.defaultLoader), there must be something going on in hibernate.


Top
 Profile  
 
 Post subject: Anyone got a workaround for this?
PostPosted: Tue Oct 05, 2004 6:58 am 
Newbie

Joined: Thu Apr 01, 2004 12:06 pm
Posts: 12
Location: Hamburg (Germany)
This could really be a problem for our application, since we will probably have to reload the Webapp quite often and the (default) Permanent Generation is already full after a handful of reloads.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Oct 05, 2004 7:31 am 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
just copy jars to web-inf/lib, latest cglib release can live outside application loader without producing memory leak after redeploy, if you can reproduce problem, do it in unit test and send it to cglib mailing list, the same is for hibernate and spring (there is a bug tracker for hibernate aka JIRA).
I hope you need to fix a problem, not to find a tool to blame using this scientific method to hunt "bugs".


Top
 Profile  
 
 Post subject:
PostPosted: Tue Oct 05, 2004 7:48 am 
Newbie

Joined: Thu Apr 01, 2004 12:06 pm
Posts: 12
Location: Hamburg (Germany)
I surely don't want to blame anyone for this. I just want to find a way to resolve our memory problem.
Since the first posts in this topic are quite old and no one has come up contradictory reply, I thought this would be kind of a "known problem".

It could be rather complicated to build a simple testcase (i.e. how do I count the number of classes loaded?), but I see if I can get the time to do it.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Oct 05, 2004 7:52 am 
Newbie

Joined: Thu Apr 01, 2004 12:06 pm
Posts: 12
Location: Hamburg (Germany)
Oh, forgot one more thing:

I already have all Jars in the WEB-INF/lib directory, never had them in the application classpath. Nevertheless there is definitely a class-memory leak. I can see it in the JMX-Console.
I'm pretty (but not completely ;-) sure that I don't hold any static references to hibernate classes (or other classes ) anywhere, so in theory they should get collected.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Oct 05, 2004 8:52 am 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
In theory any class has reference to its defining classloader "Class.getClassLoader()". if your classes are loaded by the same classloader as hibernate then there is no difference it holds one reference to classloader or more. Cglib maintains cache for generated classes, generated classes can be defined in any class loader (system classloader is not an exception too) and it references classloader for this reason (reference is soft in the latest version). Hibernate has strong indirect reference on classloader (mapped class in hashmap referenced defining classloader), but it must not produce any problems if app and hibernate is loaded by the same classloader, I hope it clears mappings in
factory "close" method and I hope you call this method too (you need it for many reasons).
I can be wrong too, if you are sure all dependencies are loaded by the same classloader then you can try to reproduce problem in unit test (some cglib unit tests are designed to test it ). We can not fix problem if we can not reproduce it.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 08, 2004 3:36 am 
Newbie

Joined: Thu Apr 01, 2004 12:06 pm
Posts: 12
Location: Hamburg (Germany)
FYI:

This seems to be a common problem with Tomcat reloading and is not necessarily related to Hibernate and CGLib. You can find a lot of readings on the Jakarta Tomcat mailing list about this. One example:

[url]http://www.mail-archive.com/tomcat-user@jakarta.apache.org/msg137004.html
[/url]
There is also a bug report which is marked as "resolved", but that didn't fix the problem in my case:
http://nagoya.apache.org/bugzilla/show_bug.cgi?id=20758

I've also read somwhere in the mailing list the advice not to reload Webapps in production environments, because of issues like these. If you need to you should restart Tomcat at whole, which will not take much longer than just reloading.

One more note: The suggested workaround in the mailing list entry to call
Code:
Introspector.flushCaches()
didn't help in my case, either (was anyway more related to an issue with log4j)


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 08, 2004 6:23 am 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
Hot deployment looks very trivial idea, just derefence classloader, but not all classes are defined by the same classloader and have indirect references to "dead" stuff, probably system class can reference some application callback too. It depends on container configuration too. The safe way is to restart container on production after deployment, just find a good time to do it. It can work, but I do not trust this feature anyway.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 13, 2005 5:31 pm 
Senior
Senior

Joined: Sat Jul 17, 2004 5:16 pm
Posts: 143
baliukas wrote:
In theory any class has reference to its defining classloader "Class.getClassLoader()".


Do you mean it can get a reference, or it has a reference like a field? When I look at the Java source in there (1.5 Sun), I do not see a field reference as you suggest, fyi...


Chris

Code:
    public ClassLoader getClassLoader() {
        ClassLoader cl = getClassLoader0();
        if (cl == null)
            return null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader ccl = ClassLoader.getCallerClassLoader();
            if (ccl != null && ccl != cl && !cl.isAncestor(ccl)) {
                sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
            }
        }
        return cl;
    }

    // Package-private to allow ClassLoader access
    native ClassLoader getClassLoader0();


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.