Fuite mémoire sur QueryCachePlan

Rédigé par gorki - - Aucun commentaire

Le problème :

L'objet QueryPlanCache d'Hibernate fait 400Mo. (Récupéré via un dump mémoire de la JVM, analysé via MAT). Normal ou pas ?

Oui et non :)

C'est normal sans aucun doute, mais alors vous avez un problème.

Le QueryPlanCache conserve l'analyse par Hibernate des requêtes HQL qui sont parsées, explosées et stockées dans un objet pour faciliter la vie à Hibernate la prochaine fois qu'il rencontre votre requête.

Si le cache utilise beaucoup de mémoire, c'est qu'il y a beaucoup de requête différentes et des grosses requêtes. (Par grosse requête j'entends : beaucoup de tables, beaucoup de conditions, etc...)

1ere raison évidente :

Vous n'utilisez pas de paramètres bindés dans vos requêtes, exemple : 

"SELECT * FROM mytable where id = " + myId;

Ce n'est pas bien, il faut, des explications ici par exemple.

2eme raison moins triviale :

Vous utilisez des grosses collections avec des clauses IN.

En HQL vous écrivez

SELECT * FROM mytable WHERE id in (:collection);

C'est bien, paramètre bindé, pas de problème. Sauf qu'en SQL les collections ça n'existe pas, donc Hibernate traduit votre requête en :

SELECT * FROM mytable WHERE id in (:item0, :item1, :item2, ...);

C'est bien, toujours des paramètres bindés, sauf que si la collection à 3 éléments ou 4 éléments, ça fait 2 requêtes

SELECT * FROM mytable WHERE id in (:item0, :item1, :item2);
SELECT * FROM mytable WHERE id in (:item0, :item1, :item2, item3);

Et donc deux entrées dans le cache.

Avec 1000 éléments (taille max de la clause IN chez Oracle), ça fait 1,5Mo par entrée du cache...

Solution :

1) Réduire la taille du cache : la taille du cache est de 2048, et peut être configuré via :

hibernate.query.plan_cache_max_size

2) Modifier la classe QueryPlanCache pour ne pas stocker les requêtes qui ont plus de X paramètres.

La 2eme solution est plus sûre au niveau mémoire et performance, mais nécessite 5 lignes de codes dans Hibernate, l'autre c'est un paramètre....

3) Si ce n'est pas trop tard, utiliser des tables temporaires et faire un join sur ces tables.

Fil RSS des articles de ce mot clé