Monday, December 30, 2013

Memory Leak Update

I have been using Hibernate with the default C3P0 connection pooling.

I have been receiving PERM GEN errors.

After reading Frank Kievet's blog about memory leaks, I started investigating.
http://frankkieviet.blogspot.com/2006/10/classloader-leaks-dreaded-permgen-space.html
http://frankkieviet.blogspot.com/2006/10/how-to-fix-dreaded-permgen-space.html

I added his code for causing a garbage collection in the perm gen and noticed that a lot of memory was being lost on each redeploy of the web app.

I downloaded the Eclipse, stand-alone Memory Analyzer (MAT).
http://www.eclipse.org/mat/

My steps to find the leak:
  1. I ran the web app and used MAT to obtain a heap dump from Tomcat. 
  2. I reran the web app again and obtained another heap dump. 
  3. I opened the dominator tree for each dump.
  4. The second dump contained the web app loader from the first dump. This class is causing the class loader memory leak.
  5. In the second dominator tree, I right-clicked the stale web app loader and selected Path to GC Roots (excluding weak references).
  6. The references listed are the ones causing the memory leak.
The leaks are usually for a third-party tool. It is important to close these tools properly. The tools I am using are Hibernate, C3P0 and MySQL. Each of these has to be closed properly. The best place for the code is in a SevletContextListener. I used several sites to find this information.
https://hibernate.atlassian.net/browse/HHH-7364
http://stackoverflow.com/questions/11872316/tomcat-guice-jdbc-memory-leak
http://docs.oracle.com/cd/E17952_01/connector-j-relnotes-en/news-5-1-23.html

I also had to update several jar files to newer versions:
hibernate-c3p0-4.1.1.Final.jar
mysql-connecto-java-5.1.28-bin.jar

package shared;

import com.mysql.jdbc.AbandonedConnectionCleanupThread;
import java.sql.Driver;
import java.sql.DriverManager;
import java.util.Enumeration;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class WebappListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        try {
            AbandonedConnectionCleanupThread.shutdown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        try {
            Enumeration enumer = DriverManager.getDrivers();
            while (enumer.hasMoreElements()) {
                DriverManager.deregisterDriver(enumer.nextElement());
            }
        } catch (java.sql.SQLException se) {
            se.printStackTrace();
        }
        shared.HibernateHelper.closeFactory();
    }
}


The HibernateHelper class is a helper class for using Hibernate. All the methods are static. It has a static variable for the session factory that already exists.

    static public void closeSessionFactory(SessionFactory factory) {
        if (factory != null) {
            if (factory instanceof SessionFactoryImpl) {
                SessionFactoryImpl sf = (SessionFactoryImpl) factory;
                ConnectionProvider conn = sf.getConnectionProvider();
               
                if (conn instanceof C3P0ConnectionProvider) {
                    ((C3P0ConnectionProvider) conn).close();
                }
            }
            factory.close();
        }
    }

    static public void closeFactory() {
        closeSessionFactory(sessionFactory);
    }

The relevant imports for this method are:

import org.hibernate.SessionFactory;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;

No comments:

Post a Comment

Followers