Announcement Announcement Module
Collapse
No announcement yet.
Spring, Hibernate OutOfMemoryError in web app Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring, Hibernate OutOfMemoryError in web app

    Hi all,

    I have an application with Spring core 2.5.6, Spring-Hibernate3 2.0.8, deployed in Tomcat 6. The OutOfMemoryError appears after about 24 hours of operation.

    Code:
    java.lang.OutOfMemoryError: Java heap space
            at java.lang.StringCoding$StringDecoder.decode(StringCoding.java:133)
            at java.lang.StringCoding.decode(StringCoding.java:173)
            at java.lang.String.<init>(String.java:443)
            at org.postgresql.core.Encoding.decode(Encoding.java:181)
            at org.postgresql.core.Encoding.decode(Encoding.java:193)
            at org.postgresql.jdbc2.AbstractJdbc2ResultSet.getString(AbstractJdbc2ResultSet.java:1893)
            at org.postgresql.jdbc2.AbstractJdbc2ResultSet.getString(AbstractJdbc2ResultSet.java:2187)
            at org.apache.tomcat.dbcp.dbcp.DelegatingResultSet.getString(DelegatingResultSet.java:225)
            at org.hibernate.type.StringType.get(StringType.java:18)
            at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:163)
            at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:154)
            at org.hibernate.type.AbstractType.hydrate(AbstractType.java:81)
            at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:2096)
            at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1380)
            at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1308)
            at org.hibernate.loader.Loader.getRow(Loader.java:1206)
            at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:580)
            at org.hibernate.loader.Loader.doQuery(Loader.java:701)
            at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
            at org.hibernate.loader.Loader.loadCollection(Loader.java:1994)
            at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:36)
            at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:565)
            at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:60)
            at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1716)
            at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:344)
            at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
            at org.hibernate.collection.PersistentSet.toArray(PersistentSet.java:171)
            at java.util.Vector.addAll(Vector.java:830)
            at net.sourceforge.seqware.common.hibernate.FindAllTheFiles.filesFromIUS(FindAllTheFiles.java:109)
            at net.sourceforge.seqware.common.hibernate.FindAllTheFiles.filesFromSample(FindAllTheFiles.java:97)
            at net.sourceforge.seqware.common.hibernate.FindAllTheFiles.filesFromExperiment(FindAllTheFiles.java:77)
            at net.sourceforge.seqware.common.hibernate.FindAllTheFiles.filesFromStudy(FindAllTheFiles.java:69)
    When I dumped the heap, I saw that 80% of memory is going into SessionFactoryObjectFactory and an enormous FastHash of tens of thousands of entries.

    I googled and found this entry: http://forum.springsource.org/showth...ring-hibernate that gives some suggestions on how to fix the problem. Nothing I do seems to fix it though.

    My application context is only being initialized once in a Singleton:

    Code:
    ApplicationContext appCtx = new ClassPathXmlApplicationContext("applicationContext.xml");
    I suspect it has to do with the transaction management system. The Hibernate layer was built separately from the web app, so in order to do lazy loading, I bind the session to the current thread. I extend the following abstract class whenever I need to work with objects that are lazily loaded.

    Code:
    public abstract class InSessionExecutions {
    
        public void runInSessionCalculations() {
            SessionFactory sessionFactory = BeanFactory.getSessionFactoryBean();
            Session session = null;
            try {
                session = bindSessionToThread(sessionFactory);
                hibernateCalls();
            } finally {
                if (session != null) {
                    unBindSessionFromTheThread(sessionFactory, session);
                }
            }
        }
    
        protected abstract void hibernateCalls();
    
        public static Session bindSessionToThread(SessionFactory sessionFactory) {
            Session session = SessionFactoryUtils.getSession(sessionFactory, true);
            TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
            return session;
        }
    
        public static void unBindSessionFromTheThread(SessionFactory sessionFactory, Session session) {
            session.flush();
            TransactionSynchronizationManager.unbindResource(sessionFactory);
            SessionFactoryUtils.releaseSession(session, sessionFactory);
        }
    }
    I tried to use getCurrentSession rather than getSession but it complained that I didn't have an open session. That led me down the path of trying to use OpenSessionInViewFilter. I put the following into my web.xml:

    Code:
        <filter>
            <filter-name>openSessionInViewFilter</filter-name>
            <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
        </filter>
    
        <filter-mapping>
            <filter-name>openSessionInViewFilter</filter-name>
            <servlet-name>/*</servlet-name>
        </filter-mapping>
    That didn't appear to do anything. I still got the same error. So I went back to using getSession.

    Next I tried to use a separate session for each transaction. The code above reflects that. But since TransactionSynchronizationManager maps the session using the SessionFactory and there's only one of those, I can't have more than one request at the same time.

    Here's a snippet of my applicationContext that is relevant to the question:

    Code:
    	<!-- Hibernate SessionFactory -->
        <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            <property name="dataSource">
                <ref local="dataSource" />
            </property>
            <property name="mappingResources">
                <list>
                    <value>net/sourceforge/seqware/common/model/Mappings.hbm.xml</value>
                </list>
            </property>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
    <!--                <prop key="hibernate.show_sql">true</prop>-->
                    <prop key="hibernate.default_entity_mode">pojo</prop>
                    <prop key="hibernate.current_session_context_class">thread</prop>
    				<!-- prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop -->
                </props>
            </property>
        </bean>
    
    	<!-- Transaction manager for the Hibernate SessionFactory. -->
        <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory">
                <ref local="sessionFactory"/>
            </property>
        </bean>
    I'm not an expert in Hibernate or in Spring and I'm at a loss of where to go from here. Does anyone have any insights?

  • #2
    1) Don't mess around with the hibernate.current_session_context_class
    2) Don't use getSession that opens NEW sessions each time it is called and you have to manage the sessions yourself, so if you aren't closing them they will remain open...
    3) You are mixing spring versions NEVER NEVER do that (you are mixing 2.5.6 and 2.0.8 don't).
    4) You have a web application why not use the ContextLoaderListener to load the application context instead of reinventing the wheel
    5) Do dependency injection don't use lookups as you are doing.
    6) There is nothing driving your transactions, you only have a transaction manager (which is also rendered useless due to 2).
    7. Your base class is what is already being done for you if you have setup transactions correctly, again don't try to reinvent the wheel

    Comment


    • #3
      Originally posted by Marten Deinum View Post
      1) Don't mess around with the hibernate.current_session_context_class
      I removed this line from the applicationContext.

      Originally posted by Marten Deinum View Post
      2) Don't use getSession that opens NEW sessions each time it is called and you have to manage the sessions yourself, so if you aren't closing them they will remain open...
      Edited: I removed all of the session management code in favor of using @Transactional (see below).

      Originally posted by Marten Deinum View Post
      3) You are mixing spring versions NEVER NEVER do that (you are mixing 2.5.6 and 2.0.8 don't).
      Changed the versions to all be 2.0.8.

      Originally posted by Marten Deinum View Post
      4) You have a web application why not use the ContextLoaderListener to load the application context instead of reinventing the wheel
      The Hibernate and Spring code are in a separate module from the web app. The Hibernate/Spring layer is used in the other facets of the application that do not involve the web application, so that is why the application context is loaded this way. Does this affect the memory usage?

      Originally posted by Marten Deinum View Post
      5) Do dependency injection don't use lookups as you are doing.
      Again, does this affect memory usage? While I'm aware that dependency injection is the preferred way to do things, I'm trying to fix this specific problem before improving other aspects of the code.

      Originally posted by Marten Deinum View Post
      6) There is nothing driving your transactions, you only have a transaction manager (which is also rendered useless due to 2).
      7. Your base class is what is already being done for you if you have setup transactions correctly, again don't try to reinvent the wheel
      The reason that this InSessionExecutions class exists is because I am re-using the Hibernate layer and need to be able to lazily load objects. In the primary Hibernate module, they use HibernateTemplate, but in my peripheral module, I was using the InSessionExecutions.

      I commented out all of the session management code in InSessionExecutions and annotated the overriding class with @Transactional as specified in http://static.springsource.org/sprin...ansaction.html. Unfortunately, I'm stuck back with the error that InSessionExecutions was intended to solve. I can't lazily instantiate collections.

      I also put this into applicationContext:

      Code:
      <tx:annotation-driven transaction-manager="transactionManager"/>
      And here's a snippet of real code:

      Code:
       public class StudyGetFiles{
      
              private int studySWA = -1;
              private FindAllTheFiles fatf = new FindAllTheFiles();
      
              public StudyGetFiles(int studySWA) {
                  this.studySWA = studySWA;
              }
      
              @Transactional
              public void hibernateCalls() {
                  StudyService ss = BeanFactory.getStudyServiceBean();
                  Study study = ss.findBySWAccession(studySWA);
                  returnValues = fatf.filesFromStudy(study);
              }
          }
      Running the code resulted in a lazy initialization exception.

      Code:
      org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: net.sourceforge.seqware.common.model.Study.experiments, no session or session was closed
          at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
          at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:350)
          at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:97)
          at org.hibernate.collection.PersistentSet.size(PersistentSet.java:139)
          at net.sourceforge.seqware.common.hibernate.FindAllTheFiles.filesFromStudy(FindAllTheFiles.java:69)
          at net.sourceforge.seqware.webservice.resources.queries.StudyIdFilesResource$StudyGetFiles.hibernateCalls(StudyIdFilesResource.java:87)
      ....
      Does this error have anything to do with the call to the 'base' Hibernate layer in StudyService.findBySWAccession(studySWA), and then subsequently attempting to query the lazily initialized fields in the returned object? StudyService uses HibernateTemplate and transaction management through the applicationContext file. If so, how would I fix it so that I can lazily load collections after the object is returned by HibernateTemplate? Do I have to reattach it or something?

      Comment


      • #4
        Why on earth change to an ancient (unsupported) version, change to 2.5.6 and remove spring-hibernate (in the newer versions that is spring-orm).

        Judging from the stacktrace you don't have transactions setup or applied correctly there is no transaction interceptor in your stack and hence no open session. Judging from your code your StudyGetFIles isn't a spring bean and as such your @Transactional is useless make it a spring bean and retrieve it from the context.

        The Hibernate and Spring code are in a separate module from the web app. The Hibernate/Spring layer is used in the other facets of the application that do not involve the web application, so that is why the application context is loaded this way. Does this affect the memory usage?
        That doesn't matter you can still use the spring classes to bootstrap your code and that would allow you to do proper dependency injection. As a temporary solution you could use your BeanFactory but have it injected the ApplicationContext as created by the ContextLoaderListener. It might affect memory usage especially if you start messing around with different classloaders which would result in duplicate beans (2 contexts being loaded).

        Comment

        Working...
        X