Java Demasiados archivos abiertos

Estoy tratando de escribir en múltiples archivos, 19 para ser exactos. Después de escribirles cientos de veces, recibo la IOException de Java: Demasiados archivos abiertos. Pero, como dije, tengo exactamente 19 archivos abiertos y los abrí todos al principio. ¿Cuál es el problema aquí? Puedo verificar que las escrituras tuvieron éxito.

edit: No estaba usando un bloque try-catch-finally. Tuve las funciones lanzar una excepción en su lugar. Ahora que puse el try-catch-finally a su alrededor, parece que lo están haciendo mejor.

La mayoría de ustedes tenía razón en que estoy abriendo más archivos de lo que pensaba. Todavía rastreando cosas. Voy a publicar una actualización después de un poco.

volver a editar: asegurándose de que todo el acceso a los archivos estaba incluido en try-catch-finally solucionó el problema. Gracias

En Linux y otras plataformas UNIX / UNIX, el sistema operativo establece un límite en la cantidad de descriptores de archivos abiertos que un proceso puede tener en un momento dado. En los viejos tiempos, este límite solía estar cableado 1 , y relativamente pequeño. Actualmente es mucho más grande (cientos / miles) y está sujeto a un límite de recursos configurable por proceso “suave”. (Busque el ulimit shell de ulimit …)

Su aplicación Java debe exceder el límite del descriptor de archivo por proceso.

Usted dice que tiene 19 archivos abiertos y que después de unos cientos de veces obtiene una IOException que dice “demasiados archivos abiertos”. Ahora, esta excepción particular SÓLO puede ocurrir cuando se solicita un nuevo descriptor de archivo; es decir, cuando está abriendo un archivo (o una tubería o un zócalo). Puede verificar esto imprimiendo stacktrace para IOException.

A menos que su aplicación se ejecute con un límite de recursos pequeño (lo que parece improbable), se deduce que debe abrir repetidamente archivos / sockets / pipes y no cerrarlos. Descubra por qué está sucediendo eso y debería poder decidir qué hacer al respecto.

FYI, el siguiente patrón es una forma segura de escribir en archivos que garantizan que no se filtrarán los descriptores de archivos.

 Writer w = new FileWriter(...); try { // write stuff to the file } finally { try { w.close(); } catch (IOException ex) { // Log error writing file and bail out. } } 

1 – Cableado, como en comstackdo en el kernel. Cambiar la cantidad de ranuras de fd disponibles requería una recomstackción … y podría hacer que haya menos memoria disponible para otras cosas. En los días en que Unix normalmente se ejecutaba en máquinas de 16 bits, estas cosas realmente importaban.

ACTUALIZAR

La forma de Java 7 es más conciso:

 try (Writer w = new FileWriter(...)) { // write stuff to the file } // the `w` resource is automatically closed 

ACTUALIZACIÓN 2

Aparentemente también puede encontrar “demasiados archivos abiertos” al intentar ejecutar un progtwig externo. La causa básica es como se describió anteriormente. Sin embargo, la razón por la que se encuentra esto en el exec(...) es que la JVM está intentando crear descriptores de archivos “de tubería” que se conectarán a la entrada / salida / error estándar de la aplicación externa.

Obviamente no estás cerrando tus descripciones de archivos antes de abrir nuevas. ¿Estás en Windows o Linux?

Para UNIX:

Como sugirió Stephen C, el cambio del valor máximo del descriptor de archivo a un valor más alto evita este problema.

Intente ver su actual capacidad de descriptor de archivo:

  $ ulimit -n 

A continuación, cambie el límite según sus requisitos.

  $ ulimit -n  

Tenga en cuenta que esto solo cambia los límites en el shell actual y cualquier proceso hijo / descendiente. Para hacer que el cambio se “admita”, debe colocarlo en el script de shell o archivo de inicialización correspondiente.

Aunque en la mayoría de los casos, el error es bastante claro de que los identificadores de archivo no se han cerrado, acabo de encontrar una instancia con JDK7 en Linux que bien … está suficientemente explotada para explicarlo aquí.

El progtwig abrió un FileOutputStream (fos), un BufferedOutputStream (bos) y un DataOutputStream (dos). Después de escribir en el dataoutputstream, el dos estaba cerrado y pensé que todo iba bien.

Sin embargo, internamente, el DOS intentó neutralizar el bos, que devolvió un error de disco lleno. Esa excepción fue consumida por DataOutputStream y, como consecuencia, el bos subyacente no se cerró, por lo tanto, el fos todavía estaba abierto.

En una etapa posterior, ese archivo se renombró de (algo con un .tmp) a su nombre real. De este modo, los rastreadores del descriptor de archivo java perdieron la pista del .tmp original, ¡pero aún estaba abierto!

Para solucionar esto, primero tuve que lavar el DataOutputStream, recuperar la IOException y cerrar FileOutputStream yo mismo.

Espero que esto ayude a alguien.

Recientemente, tuve un progtwig de procesamiento por lotes de archivos, sin duda cerré cada archivo en el bucle, pero el error sigue ahí.

Y luego, resolví este problema recolectando basura con entusiasmo cada cientos de archivos:

 int index; while () { try { // do with outputStream... } finally { out.close(); } if (index++ % 100 = 0) System.gc(); }