Analyse des performances applicatives – synchronisation de code

Parmi les problèmes de performance que j’ai eu à solutionner chez des clients ces derniers mois, il y a ceux relatifs aux contentions applicatives causées par une mauvaise utilisation des directives de synchronisation disponibles dans le langage Java.

J’ai remarqué à plusieurs reprises que certains développeurs avaient tendance à sur protéger des sections de code. Parfois ces sections de codes protégées faisaient directement ou indirectement des appels à la base de données, d’autres fois il s’agissait de blocs de code exécutés très souvent.

Les problèmes de performance issus de ce type de mauvaises pratiques ne se détectent que lorsque l’on soumet l’application à une forte charge ou dans un contexte de production sur lequel il est particulièrement difficile d’intervenir à posteriori. C’est pourquoi la mise en place d’une solution APM (Application Performance Monitoring) est intéressante dans la mesure où la plupart des solutions APM permettent de journaliser le contexte d’apparition du dysfonctionnement et permettent avec plus ou moins de simplicité de remonter à la cause du problème.

Sans ces solutions de monitoring applicatif, il existe une autre méthode pour détecter des problèmes de contention qui ne doit être utilisée que dans un environnement de tests. Cette méthode est basée sur la prise répétitive, et légèrement espacée dans le temps, de thread dump (kill -3 <pid> sur unix et ctrl + break sur windows). Le thread dump est un vidage de la pile de threads du processus java. La comparaison des différents ‘snapshot’ permet d’identifier le cheminement du code (la trace d’exécution) qui conduit à une mise en attente du code. Pour que le diagnostic soit efficace, il faut nécessairement que cette prise de mesure soit réalisée sous des conditions de charge qui permettent de mettre en évidence le dysfonctionnement. En effet, plus on simule d’utilisateurs plus le temps passé à attendre pour pouvoir exécuter un bloc synchronisé devient important.

Extrait de thread dump:
« http-127.0.0.1-8080-11 » daemon prio=6 tid=0x0d2b7000 nid=0x3d50 in Object.wait() [0x0f5be000]    java.lang.Thread.State: WAITING (on object monitor)
                        at java.lang.Object.wait(Native Method)
                        at java.lang.Object.wait(Object.java:485)
                        at com.ibatis.common.util.Throttle.increment(Throttle.java:44)
                        – locked <0x04ab14b0> (a java.lang.Object)
                        at com.ibatis.common.util.ThrottledPool.pop(ThrottledPool.java:24)
                        at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.popSession
SqlMapExecutorDelegate.java:539)
                        at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.<init>
SqlMapSessionImpl.java:34)
                        at com.ibatis.sqlmap.engine.impl.SqlMapClientImpl.getLocalSqlMapSession
SqlMapClientImpl.java:169)
                        at com.ibatis.sqlmap.engine.impl.SqlMapClientImpl.startTransaction
SqlMapClientImpl.java:87)

Dans cet extrait la méthode com.ibatis.common.util.Throttle.increment(..) est bloquée et attend la disponibilité du moniteur pour exécuter la méthode. Ci dessous un extrait de la méthode increment().

public void increment()
    {
        synchronized(LOCK)
        {
           …
         }
    }

Si vous utilisez une solution APM vous pouvez identifier les contentions en comparant le temps CPU utilisé pour l’exécution d’une méthode et le temps d’exécution de la méthode. Cette dernière métrique inclut le temps CPU, I/O, les temps d’attente des systèmes externes, les temps d’interruption causés par les collections java (GC),  et le temps passé à attendre l’exécution des blocs synchronisés. Cette méthode n’est pas fiable à 100% car il y a une multitude d’aléas qui peuvent conduire à un temps d’exécution plus long. Malgré tout, la différence entre ces 2 valeurs devrait vous permettre de pointer du doigt l’origine probable du dysfonctionnement surtout si vous savez que la méthode soupçonnée de dégrader les performances, ne réalise pas d’appel à un système externe  (par exemple une base de données) ou ne réalise pas d’I/O . En théorie le temps CPU doit rester stable tandis que le temps d’exécution de la méthode doit augmenter proportionnellement au temps d’attente lié à la contention.

http://www.jperf.com

Publicités

A propos jlerbsc

founder of JavaPerf Consulting Http://www.jperf.com
Cet article a été publié dans performance. Ajoutez ce permalien à vos favoris.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s