1

I have a Java web application running on Tomcat 7 - jdk1.7

This application uses Spring 4.1.5.RELEASE and Hibernate 4.2.2.Final

My problem is a OutOfMemoryException of the Heap space on building section factory

This is my static method that opens a SessionFactory

public class GenericDAO {

    public static SessionFactory sessionFactory = null;
    public static ServiceRegistry serviceRegistry = null;

    Transaction tx = null;

    public static SessionFactory createSessionFactory() {
        Configuration configuration = new Configuration();
        configuration.configure();
        serviceRegistry = new ServiceRegistryBuilder().applySettings(
            configuration.getProperties()). buildServiceRegistry();
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        return sessionFactory;
    }
}

And this is an example of DAO

public class SpecificDAO extends GenericDAO {
    public int save(MyObject item) {
        Session session = createSessionFactory().openSession();
        try {
            tx = session.beginTransaction();
            session.save(item);
            tx.commit();
            return item.getId();
        } catch (HibernateException e) {
            if (tx != null) tx.rollback();
                 e.printStackTrace();
        } finally {
            session.close();
        }
        return -1;
    }
}

The error occurs at the line containing

sessionFactory = configuration.buildSessionFactory(serviceRegistry);

The problem doesn't occur immediately at the deploy, but after 2 o 3 days of usage

This is my Hibernate.cfg.xml

<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
 <hibernate-configuration>
<session-factory>
    <property name="connection.url">jdbc:sqlserver://192.168.XX.XXX:1433;databaseName=DatabaseName</property>
    <property name="connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
    <property name="connection.username">username</property>
    <property name="connection.password">password</property>
    <mapping class="it.company.client.project.hibernate.MyObject"/>

    <!-- DB schema will be updated if needed -->
    <!-- <property name="hbm2ddl.auto">update</property> -->
</session-factory>
</hibernate-configuration>

4 Answers4

2

You have to create the session factory only once as it is a heavy weight object, refer to the hibernate documentation for its details.

Here is the sample code from the doc on how it should be created:

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {

    private static final SessionFactory sessionFactory = buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            return new Configuration().configure().buildSessionFactory(
                new StandardServiceRegistryBuilder().build() );
        }
        catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

}
Chaitanya
  • 15,403
  • 35
  • 96
  • 137
1

It is better idea to flush and clear the session after used, you can use both

session.flush();
session.clear();

for more information link1 and link2

Community
  • 1
  • 1
nilesh virkar
  • 429
  • 2
  • 12
1

You are creating a SessionFactory object for every save() call.

i.e you are creating a new SessionFactory repeatedly for every save() call but not closing the existing SessionFactory objects in memory.

How many times save() is called ? the same no of SessionFactory will be in memory, which causes the memory leak.

SessionFactory are heavy weight objects, so you'd create at application initialization. You can create a SingleTon to instantiate SessionFactory.

Saravana
  • 12,647
  • 2
  • 39
  • 57
  • But this answer: https://stackoverflow.com/a/14411979/4759176 says that SesssionFactory is *already* a singleton. Then it's not true, then? – parsecer Dec 18 '19 at 11:47
1

Avoid instantiation of SessionFactory object on every DAO action. It is very slow and causes memory leaks. Better explained in this answer

If you're using Spring anyway, better to delegate to Spring work with SessionFactory, transactions and handling SQL exceptions. For example, your save() method will reduce to one line of code sessionFactory.getCurrentSession().save(item); Manual transaction open/commit/rollback should be replaced with @Transactional attribute. Also, usually better place transactions on whole service method, not on every single DAO method, but it depends of business logic.

Here example how to configure spring context for work with Hibernate (just first article for google)

I slightly adopted this example for current question

@Repository
public class SpecificDAO {
    private SessionFactory sessionFactory;

    @Autowired
    public SpecificDAO(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Transactional(propagation=Propagation.REQUIRED)
    public int save(MyObject item) {
        try{
            sessionFactory.getCurrentSession().save(item);
        }catch (HibernateException e) {
            return -1;
        }
    }
}

Spring configuration

<context:annotation-config/>
<context:component-scan base-package="it.company.client.project"/>
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
    <property name="url" value="jdbc:sqlserver://192.168.XX.XXX:1433;databaseName=DatabaseName"/>
    <property name="username" value="username"/>
    <property name="password" value="password"/>
</bean>
<bean id="sessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="annotatedClasses">
            <list>
                <value>it.company.client.project.hibernate.MyObject</value>
            </list>
        </property>

        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
                <prop key="hibernate.connection.provider_class">org.hibernate.connection.DatasourceConnectionProvider</prop>
                <prop key="hibernate.show_sql">false</prop>
                <!--prop key="hibernate.hbm2ddl.auto">update</prop-->
            </props>
        </property>
</bean>
<tx:annotation-driven />
<bean id="transactionManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
Community
  • 1
  • 1
user1516873
  • 5,060
  • 2
  • 37
  • 56