2012-04-17 20 views
8

Recibo diariamente errores de OutOfMemory en una nueva versión de mi aplicación. Tenemos 1,5 GB de almacenamiento asignado para Tomcat.JPA Hibernate DBCP Tomcat OutOfMemory

Utilizando el analizador de memoria Eclipse (http://www.eclipse.org/mat/) que tiene la siguiente debajo de la ruta más corta Acumulación

org.apache.tomcat.dbcp.pool.impl.CursorableLinkedList$Listable @ 0xa1566cc8 
    _head org.apache.tomcat.dbcp.pool.impl.CursorableLinkedList @ 0xa1566ca8 
     _pool org.apache.tomcat.dbcp.dbcp.AbandonedObjectPool @ 0xa1566c38 
     connectionPool org.apache.tomcat.dbcp.dbcp.BasicDataSource @ 0xa1566980 
      dataSource org.springframework.orm.jpa.JpaTransactionManager @ 0xa0b01760 
        <Java Local> java.lang.Thread @ 0xa4005900 ajp-8141-5 Thread 

inspección más a fondo de esta muestra una gran cantidad de cadenas duplicadas que son consultas Hibernate. En la pantalla de inicio de mi aplicación, cargo una lista de documentos. Existe un duplicado de la consulta 8,241 veces en el volcado de pila.

También noté que 1 GB del montón está contenido en org.apache.tomcat.dbcp.dbcp.AbandonedObjectPool. Esta consulta de documento estaba cargando los datos binarios del documento. El documento que está cargando es de alrededor de 1 MB. Esto me hace sospechar que la Lista no está siendo limpiada por el recolector de basura. Eliminaremos datos innecesarios de la consulta, pero todavía me preocupa que los objetos se queden.

Estoy usando JPA, Hibernate y Spring. Estoy usando @Transactional(readOnly=true) en el método que obtiene la lista de documentos. Aquí está mi configuración Spring para la fuente de datos:

<jee:jndi-lookup jndi-name="jdbc/MyDB" id="myDataSource"/> 

    <bean id="myEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 

     <property name="dataSource" ref="myDataSource"/> 
     <property name="persistenceUnitName" value="WebPU"/> 
     <property name="persistenceProvider"> 
      <bean class="org.hibernate.ejb.HibernatePersistence" /> 
     </property> 
     <property name="jpaVendorAdapter"> 
      <bean 
       class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
       <property name="database" value="SQL_SERVER" /> 
       <property name="showSql" value="false" /> 
       <property name="generateDdl" value="false" /> 
      </bean> 
     </property> 

    </bean> 

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
     <property name="entityManagerFactory" ref="myEmf" /> 
    </bean> 

Estoy utilizando Tomcat para proporcionar la agrupación de conexiones. Aquí está mi configuración:

<Resource auth="Container" driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver" initialSize="20" 
    logAbandoned="true" maxActive="100" maxIdle="30" maxWait="10000" name="jdbc/MyDB" password="pass" poolPreparedStatements="true" removeAbandoned="true" removeAbandonedTimeout="30" 
    type="javax.sql.DataSource" 
    url="jdbc:sqlserver://devmssql;databaseName=MY_DEV;responseBuffering=adaptive;port=1444" 
    username="user"/> 

La capa de servicio tiene @Transactional. Mi APP de consulta es el siguiente:

public List<Document> getDocs(int cId, int lId, int ldId) { 
     CriteriaBuilder queryBuilder = getEntityManager().getCriteriaBuilder(); 
     CriteriaQuery<Document> select = queryBuilder.createQuery(Document.class); 

     Root<Document> from = select.from(Document.class); 

     select.where(
       queryBuilder.and(queryBuilder.equal(from.get(Document_.cId), cId), 
       queryBuilder.equal(from.get(Document_.lId), lId), 
       queryBuilder.equal(from.get(Document_.ldId), ldId))); 




     TypedQuery<Document> tq = getEntityManager().createQuery(select); 

     final List<Document> rl = tq.getResultList(); 

     return rl; 
    } 

¿Qué debería estar buscando para ayudar a identificar la causa de la pérdida de memoria? ¿Hay alguna configuración de DBCP, Hibernate o Spring que pueda estar contribuyendo a esto? ¿Hay algo que notes en el código de consulta JPA que podría estar contribuyendo?

+0

¿Cómo se implementa getEntityManager()? ¿se inyectó EntityManager con la anotación @PersistenceContext? – samlewis

+0

Sí. Spring está inyectando, esto no se está ejecutando en un servidor JavaEE. Estamos usando Tomcat. – Allan

Respuesta

0

Intente borrar el caché de EntityManager antes de regresar del método. Primero llame al método EntityManager.flush() y luego clean(). Esto podría ayudar si su EntityManager "vive" mucho tiempo y es utilizado por muchas operaciones de bases de datos.

Si usa Spring puede dejar de usar Hibernate para situaciones problemáticas e ir con JDBC simple. Spring nos brinda JDBCTemplate para que trabaje fácilmente con las consultas y también proporciona una interfaz de administración de transacciones común, para que pueda mezclar las operaciones de Hibernate con consultas JDBC.

1

Recomiendo encarecidamente utilizar VisualVM o jProfiler para identificar la fuga. Es mi humilde opinión la forma más fácil.