streams parallel into instream divided baeldung java stream java-8

java - parallel - ¿Fuga de recursos en Files.list(ruta dir) cuando la secuencia no está cerrada explícitamente?



stream parallel java 8 (3)

Con respecto a la parte IDE: Eclipse realiza un análisis de fugas de recursos basado en variables locales (y expresiones de asignación de recursos explícitas), por lo que solo tiene que extraer el flujo a una variable local:

Stream<Path> files =Files.list(Paths.get(destination)); files.forEach(path -> { // To stuff });

Entonces Eclipse te dirá

Fuga de recursos: ''archivos'' nunca se cierran

Detrás de escena, el análisis funciona con una cascada de excepciones:

  1. Todo Closeable s necesita cierre
  2. java.util.stream.Stream (que se puede cerrar) no necesita cerrarse
  3. Todas las secuencias producidas por métodos en java.nio.file.Files necesitan cerrarse

Esta estrategia se desarrolló en coordinación con el equipo de la biblioteca cuando discutieron si Stream debería ser AutoCloseable o no.

Recientemente escribí una pequeña aplicación que verificaba periódicamente el contenido de un directorio. Después de un tiempo, la aplicación se bloqueó debido a demasiados manejadores de archivos abiertos. Después de un poco de depuración, encontré el error en la siguiente línea:

Files.list(Paths.get(destination)).forEach(path -> { // To stuff });

Luego revisé el javadoc (probablemente debería haberlo hecho antes) para Files.list y encontré:

* <p> The returned stream encapsulates a {@link DirectoryStream}. * If timely disposal of file system resources is required, the * {@code try}-with-resources construct should be used to ensure that the * stream''s {@link Stream#close close} method is invoked after the stream * operations are completed

Para mí, la "eliminación oportuna" todavía parece que los recursos se liberarán eventualmente, antes de que la aplicación se cierre. Revisé el código JDK (1.8.60) pero no pude encontrar ninguna pista sobre los manejadores de archivos abiertos por Files.list se lanzaron nuevamente.

Luego creé una pequeña aplicación que llama explícitamente al recolector de basura después de usar Files.list esta forma:

while (true) { Files.list(Paths.get("/")).forEach(path -> { System.out.println(path); }); Thread.sleep(5000); System.gc(); System.runFinalization(); }

Cuando verifiqué los manejadores de archivos abiertos con lsof -p <pid> todavía podía ver la lista de manejadores de archivos abiertos para que "/" se alargara más y más.

Mi pregunta ahora es: ¿hay algún mecanismo oculto que eventualmente deba cerrarse y ya no se utilicen los manejadores de archivos abiertos en este escenario? ¿O estos recursos, de hecho, nunca se disponen y el javadoc es un poco eufemístico cuando se habla de "eliminación oportuna de los recursos del sistema de archivos"?


Si cierra la secuencia, Files.list() sí cierra el DirectoryStream subyacente que utiliza para transmitir los archivos, por lo que no debería haber pérdida de recursos siempre que cierre la secuencia.

Puede ver dónde se cierra DirectoryStream en el código fuente de Files.list() aquí:

return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false) .onClose(asUncheckedRunnable(ds));

La clave a entender es que un Runnable se registra con Stream usando Stream::onClose que se llama cuando se cierra el stream. Ese Runnable es creado por un método de fábrica, como asUncheckedRunnable que crea un Runnable que cierra el recurso pasado a él, traduciendo cualquier IOException lanzada durante el close() a un UncheckedIOException

Puede asegurarse con seguridad de que DirectoryStream está cerrado asegurándose de que Stream se cierre de esta manera:

try (Stream<Path> files = Files.list(Paths.get(destination))){ files.forEach(path -> { // Do stuff }); }


List<String> fileList = null; try (Stream<Path> list = Files.list(Paths.get(path.toString()))) { fileList = list.filter(Files::isRegularFile).map(Path::toFile).map(File::getAbsolutePath) .collect(Collectors.toList()); } catch (IOException e) { logger.error("Error occurred while reading email files: ", e); }