8 votes

JPA Hibernate DBCP Tomcat OutOfMemory

Je reçois quotidiennement des erreurs OutOfMemory dans une nouvelle version de mon application. Nous avons 1,5 Go de tas alloué pour Tomcat.

Utilisation de l'analyseur de mémoire Eclipse ( http://www.eclipse.org/mat/ ) J'ai obtenu les résultats suivants sous le chemin d'accumulation le plus court

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 

Un examen plus approfondi de ces données montre un grand nombre de chaînes en double qui sont des requêtes Hibernate. Sur l'écran d'accueil de mes applications, je charge une liste de documents. Un doublon de la requête existe 8 241 fois dans le vidage du tas.

J'ai également remarqué que 1 Go du tas est contenu dans org.apache.tomcat.dbcp.AbandonedObjectPool. Cette requête de document chargeait les données binaires du document. Le document qu'elle charge est d'environ 1 Mo. Cela me fait penser que la liste n'est pas nettoyée par le ramasseur d'ordures. Nous allons supprimer les données inutiles de la requête, mais je suis toujours préoccupé par le fait que des objets restent en place.

J'utilise JPA, Hibernate et Spring. J'utilise @Transactional(readOnly=true) sur la méthode qui récupère la liste des documents. Voici ma configuration Spring pour la source de données :

<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>

J'utilise Tomcat pour fournir la mise en commun des connexions. Voici ma configuration :

<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 couche service possède le @Transactional. Ma requête JPA ressemble à ceci :

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;
    }

Que dois-je rechercher pour aider à identifier la cause première de la fuite de mémoire ? Y a-t-il des paramètres DBCP, Hibernate ou Spring qui pourraient contribuer à ce problème ? Avez-vous remarqué quelque chose dans le code de la requête JPA qui pourrait y contribuer ?

1voto

kromit Points 1298

Je recommande vivement d'utiliser VisualVM o jProfiler pour identifier la fuite. C'est, à mon avis, le moyen le plus simple.

0voto

Piotr Kochański Points 8162

Essayez de vider le cache d'EntityManager avant de revenir de la méthode. Appelez d'abord la méthode EntityManager.flush(), puis clean(). Cela peut être utile si votre EntityManager "vit" longtemps et qu'il est utilisé par de nombreuses opérations de base de données.

Si vous utilisez Spring, vous pouvez renoncer à Hibernate pour les situations problématiques et opter pour un simple JDBC. Spring nous donne JDBCTemplate pour travailler facilement avec les requêtes et fournit également une interface commune de gestion des transactions, de sorte que vous pouvez mélanger les opérations Hibernate avec les requêtes JDBC.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X