Passer au contenu principal

Java dispose d’un Garbage Collector. Celui-ci, très pratique, ne fonctionne que pour les objets propres à la JVM, et pas quand une ressource externe entre en jeu, notamment les entrées/sorties vers les fichiers. Si vous ne faites pas attention à ce dernier point, c’est une erreur TooManyOpenFiles assurée, avec probable redémarrage de la JVM à la clef si vous n’avez pas activé JMX.

Malheureusement pour nous, fermer ses flux fichiers en Java est moins trivial qu’il n’y paraît…

Dans la suite on joue uniquement avec des Stream en entrée, mais en fait la même chose s’applique pour ceux en sortie comme les OutputStream.

La mauvaise manière de fermer ses flux

Le code suivant est complètement cassé :


try {
   InputStream is = new FileInputStream(...);
   InputStreamReader ir = new InputStreamReader(is);
   BufferedReader br = new BufferedReader(ir);

   // ...

   br.close();
   ir.close();
   is.close();
] catch (IOException e) {
   e.printStackTrace();
}

La raison ne vient pas de la gestion d’exception, mais bien de la gestion des flux. En effet si une erreur survient entre l’ouverture des flux et leur fermeture, rien ne garantit que ces derniers seront correctement fermés, ou du moins pas avant le prochain passage du GC qui peut être dans… longtemps. Dès lors on court là encore droit à l’erreur TooManyOpenFiles.

La bonne manière de faire pour Java 6 et inférieurs

La bonne manière de faire est hélas bien plus verbeuse. Il s’agit d’écrire le code suivant :


InputStream is = null;
InputStreamReader ir = null;
BufferedReader br = null;

try {
   is = new FileInputStream(...);
   ir = new InputStreamReader(is);
   br = new BufferedReader(ir);

   // ...
] catch (IOException e) {
   e.printStackTrace();
} finally {
   if (br != null) {
      try {
         br.close();
      } catch (IOException e) {}
   }
   if (ir != null) {
      try {
         ir.close();
      } catch (IOException e) {}
   }
   if (is != null) {
      try {
         is.close();
      } catch (IOException e) {}
   }
}

Alors oui c’est lourd, ultra verbeux, pénible, vous pouvez dire ce que vous voulez. Dans la librairie commons-io d’Apache IOUtils.closeQuietly peut améliorer quelque peu le code au niveau de la fermeture des fichiers mais quand même, ça reste lourd.

C’est d’ailleurs une des raisons qui explique pourquoi beaucoup de gens ne gèrent pas correctement les flux en Java.

La méthode Java 7 et ultérieur : le try-with-resources

Java 7 introduit un nouveau concept dénommé try-with-resource. Pour en profiter il suffit que votre objet implémente l’interface Closeable, ce qui est justement le cas de nos Stream.

Dès lors le code vu plus haut se traduit de la manière suivante :


try (InputStream is = new FileInputStream(...);
     InputStreamReader ir = new InputStreamReader(is);
     BufferedReader br = new BufferedReader(ir);) {

   // ...
] catch (IOException e) {
   e.printStackTrace();
}

On remarque notamment qu’il n’est plus nécessaire d’invoquer manuellement la méthode close() puisque le try-with-resource s’en charge pour nous. Ca allège quand même violemment le code. Cela dit il est important d’ouvrir les streams un par un et pas en appels imbriqués du type new BufferedReader(new InputStreamReader(new FileInputStream(...))). En effet, si l’ouverture du BufferedReader venait à échouer le FileInputStream ne serait pas fermé !

Mais bon, force est d’améliorer que ça améliorer nettement la lisibilité du code.

Cet article vous a plu ? Vous aimerez sûrement aussi :

Julien
Moi c’est Julien, ingénieur en informatique avec quelques années d’expérience. Je suis tombé dans la marmite étant petit, mon père avait acheté un Apple – avant même ma naissance (oui ça date !). Et maintenant je me passionne essentiellement pour tout ce qui est du monde Java et du système, les OS open source en particulier.

Au quotidien, je suis devops, bref je fais du dév, je discute avec les opérationnels, et je fais du conseil auprès des clients.

Son Twitter Son LinkedIn

Rejoignez la discussion Un commentaire

Laisser un commentaire