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 ?