Resource leak in Files.list(Path dir) when stream is not explicitly closed?

后端 未结 3 1126
广开言路
广开言路 2020-12-15 03:51

I recently wrote a small app that periodically checked the content of a directory. After a while, the app crashed because of too many open file handles. After some debugging

相关标签:
3条回答
  • 2020-12-15 04:27
       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);
      }
    
    0 讨论(0)
  • 2020-12-15 04:36

    Regarding the IDE part: Eclipse performs resource leak analysis based on local variables (and explicit resource allocation expressions), so you only have to extract the stream to a local variable:

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

    Then Eclipse will tell you

    Resource leak: 'files' is never closed

    Behind the scenes the analysis works with a cascade of exceptions:

    1. All Closeables need closing
    2. java.util.stream.Stream (which is Closeable) does not need closing
    3. All streams produced by methods in java.nio.file.Files do need closing

    This strategy was developed in coordination with the library team when they discussed whether or not Stream should be AutoCloseable.

    0 讨论(0)
  • 2020-12-15 04:44

    If you close the Stream, Files.list() does close the underlying DirectoryStream it uses to stream the files, so there should be no resource leak as long as you close the Stream.

    You can see where the DirectoryStream is closed in the source code for Files.list() here:

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

    The key thing to understand is that a Runnable is registered with the Stream using Stream::onClose that is called when the stream itself is closed. That Runnable is created by a factory method, asUncheckedRunnable that creates a Runnable that closes the resource passed into it, translating any IOException thrown during the close() to an UncheckedIOException

    You can safely assure that the DirectoryStream is closed by ensuring the Stream is closed like this:

    try (Stream<Path> files = Files.list(Paths.get(destination))){
        files.forEach(path -> {
             // Do stuff
        });
    }
    
    0 讨论(0)
提交回复
热议问题