Fuite mémoire dans java.text.DecimalFormat de GnuClasspath

Aujourd’hui, un collègue me montre du code qui utilise java.text.DecimalFormat de GnuClasspath, la JCL du projet GNU, et qui plante avec une java.lang.OutOfMemoryError.

L’objectif est de formater un double suivant un nombre de décimal, ce que fait très bien la classe java.text.DecimalFormat.

Voici la stacktrace complète de mon code de test :

Exception in thread "main" java.lang.OutOfMemoryError
at java.util.ArrayList.ensureCapacity(ArrayList.java:176)
at java.util.ArrayList.add(ArrayList.java:340)
at java.text.DecimalFormat.addAttribute(DecimalFormat.java:2236)
at java.text.DecimalFormat.handleFractionalPart(DecimalFormat.java:2074)
at java.text.DecimalFormat.formatInternal(DecimalFormat.java:1942)
at java.text.DecimalFormat.format(DecimalFormat.java:387)
at java.text.NumberFormat.format(NumberFormat.java:818)
at trash.MyTrash.testNewDecimal(MyTrash.java:34)
at trash.MyTrash.(MyTrash.java:18)
at trash.MyTrash.main(MyTrash.java:8)

Commençons les investigations de java.text.DecimalFormat.

J’isole au strict minimum le code pour en faire un test minimum et je m’aperçois que java.text.DecimalFormat peut être utilisé soit avec une nouvelle instance soit depuis un singleton.

Je vais donc tester dans une boucle infinie les deux possibilités car le problème pourrait venir de là.

public void testNewDecimal() {
  DecimalFormat df = new DecimalFormat("#.0000");
  while (true) {
    df.format(0.0);
  }
}
public void testSingletonDecimal() {
  DecimalFormat df = ((DecimalFormat) DecimalFormat.getInstance());
  df.applyPattern("#.0000");
 
  while (true) {
    df.format(0.0);
  }
}

Sur une config [CONFIG_ARM926EJ-S] avec JamVM 1.5.4 / GnuClasspath 0.98 / Java 1.3 le code plante dans les deux cas avec un java.lang.OutOfMemoryError en moins de 10 minutes !

En analysant le code source de GnuClasspath, la classe DecimalFormat gère un cache interne dans un ArrayList qui n’est jamais vidé ! Ce qui est confirmé par la stackstrace qui montre que la vérification de la capacité de la collection échoue sur un appel de la méthode ensureCapacity.

De plus, ce bug est identifié depuis 2007 sous le numéro 30977 mais n’a jamais été corrigé. Dommage car cela m’a fait perdre un peu de temps aujourd’hui…

Au final, le mieux est d’écrire soit même une petite méthode qui formate un nombre décimal suivant ces besoins, cela vous fera un petit exercice de manipulation des chaînes de caractères !

Laisser un commentaire