03 février 2010

Erreurs de débutant en Java: fermer les fichiers !

Les développeurs Java débutants (j'en vois qui ont de l'expérience et qui continuent de faire cette erreur chaque jour ...) considèrent souvent qu'il est inutile de libérer les ressources allouées.

Comme il s'agit de Java et que le Garbage Collector veille, il est trop souvent considéré que la mémoire se libère de façon automatique, ce qui n'est que partiellement vrai.

Une des erreurs courantes consiste à ne pas fermer les fichiers ouverts, que ce soit en lecture ou en écriture.

Considérons la méthode suivante qui consiste à copier un fichier par bloc de 1024 octets sur un autre filesystem:

public static boolean copyTo(File src, File dest, boolean close)
throws IOException {
FileOutputStream out = null;
FileInputStream in = null;
try {
if (!src.exists())
throw new FileNotFoundException(src.getAbsolutePath());
if (dest.exists() || !dest.createNewFile())
return false;
out = new FileOutputStream(dest);
in = new FileInputStream(src);
int len = 1024;
int nread;
byte[] b = new byte[len];
for (;;)
if ( (nread = in.read(b, 0, len)) <= 0) break; else out.write(b, 0, nread); return true; } finally { if (close) { try { out.close(); } catch (IOException ioe) { } try { in.close(); } catch (IOException ioe) { } } } }


Le paramètre close permet de fermer ou non les flux ouverts (input et output).

Si on appelle cette méthode, par exemple 2048 fois avec un nom de fichier de destination qui change et le paramètre close à false, on obtient l'erreur suivante:

java.io.IOException: Too many open files
at java.io.UnixFileSystem.createFileExclusively(Native Method)
at java.io.File.createNewFile(File.java:883)
at FileNotClosed.copyTo(FileNotClosed.java:36)
at FileNotClosed.main(FileNotClosed.java:17)


Et on n'arrive qu'à copier 510 fichiers, ce qui veux dire qu'on en a ouvert 2*510 soit 1020 fichiers, parce que le max open files défini par process sur cette machine Linux est de 1024:

ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 32768
max locked memory (kbytes, -l) 32
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 32768
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited

Les autres fichiers ouverts le sont par la JVM elle même.

En conséquence de quoi, il est primordial de fermer TOUS les fichiers ouverts après leur utilisation, et ce de façon systématique dans le bloc finally afin que la fermeture soit faites dans tous les cas.

Evidemment si un System.exit() est appelé, le bloc finally correspondant lui ne le sera pas.

Ce genre d'erreur se voit tous les jours, et dans un serveur d'applications par exemple, cela fait des ravages !

Autres posts liés à Développement / Logiciel / Java / Shell / C:

Heure d'été, Classe Date, JDK 1.5 et TimeZone
Un très ancien bug non découvert jusque là
pop3/tcp server failing (looping), service terminated
De l’usage des programmes d’exemple
JavaMail en IMAP avec un serveur Exchange

Libellés : , , , , , , , , , , , , ,

1 commentaires:

Blogger PY a dit...

Appeler System.gc() dans le bloc finally de la méthode copyTo(...) aide à réduire le problème, parce que dans ce cas les méthodes protected void finalize() des classes FileInputStream et FileOutputStream sont appelées puisqu'on les forcent à l'être.

Néanmoins, cela ne constitue pas une solution, car l'appel au Garbage Collector est très coûteux.

9:32 PM  

Enregistrer un commentaire

Abonnement Publier les commentaires [Atom]

<< Accueil