Si vous êtes un développeur JAVA vous avez sûrement déjà eu besoin de paramétrer le garbage collector pour qu'il colle au mieux à votre application.

Pour ma part je travaille sur une application de trading ayant une architecture distribuée qui utilise une bonne pelletée de serveur (plus de 100) et qui se doit d'avoir des temps de réponses rapides. Nous sommes donc censés avoir des JVMs et des GC configurés de telle sorte que les processes soient le moins possible interrompus par la garbage collection.

Ainsi quand on livre un process en prod on le livre avec ses paramètres de GC. Jusque là tout va bien. Malheureusement certains de nos process ont une dizaine d'années. Ils ont donc été migrés de serveurs en serveurs au fil des ans (plus de ram, plus de coeurs, plus de Hz, ...) sans que personne ne se préoccupe de vérifier un tant soit peu les paramètres de GC. Dans le meilleur des mondes tout aurait pu bien se passer, mais non...

J'ai découvert avec stupeur qu'un process censé être "temps réel" avait 14,4Mo de Young pour 1.970Mo de Old !!!! Echec. Le plus fun est que ce process tourne en fault-tolerance[1] et que le process de backup a lui 115Mo de Young. Et oui, les deux serveurs où tournent les instances sont différents. Ils ont tous les deux 10Go de ram, mais l'un est bi-proc quad-core et l'autre est bi-proc bi-core et bien sûr les procs ont des fréquences différentes.

Que s'est il passé ? Et bien c'est simple au bout de 2h d'uptime le process de backup est arrivé à bout de sa heap et a décidé de rajouter 100Mo de heap (il était justement 100Mo de son Xmx) et il a tout mis dans sa Young. Ca pourrait être le hasard, mais ce schéma se reproduit presque tous les jours (en fonction de la charge de la journée). Et cela se produit sur tous les couples que nous avons de ce process qui tournent sur des serveurs différents (6 couples sur 12 serveurs). Les serveurs les plus rapides utilisent plus de young. Cela est dû au parrallel GC que nous utilisons et au fait que la JVM change ses paramètres en fonction des performances du process (et donc de la machine)[2]. En effet ajouter de la mémoire impacte les temps de GC, et donc le throughput de l'application, sur les serveurs lents la VM mets plus de mémoire dans la Old, et sur les rapides elle en met plus dans la Young, je trouve ça idiot puisque du coup les collections majeurs sont plus fréquentes sur les vieux serveur et l'application est donc plus lente... échec.

En conclusion quand vous fixez vos paramètres de GC pour un serveur, faites attention ils ne seront peut être pas valide pour un autre serveur même s'il est plus puissant... Ensuite si vous avez définit un Xmx n'hésitez pas à mettre un Xms égal au Xmx et à définir un ratio young/old cohérent avec votre application. (attention le ratio est appliqué au démarrage et dépend de la heap à ce moment, donc Xmx=Xms est important !!!)

Notes

[1] il tourne en 2 exemplaires, un actif, un inactif. Quand l'actif crash, l'inactif devient actif et prend le relais

[2] http://docs.oracle.com/javase/1.5.0/docs/guide/vm/gc-ergonomics.html