2017-10-18 85 views
0

我在我的应用程序中遇到了一个奇怪的错误。我已经通过解决方法解决了这个问题,但我仍然很好奇为什么会发生这种错误。Files.walkFileTree泄漏目录描述符与自定义FileVisitor

下面给出了一个自定义FileVisitor的示例,该FileVisitor删除了它所经历的空目录。如果这些目录不是空的,并且它仍然遍历这些目录,它将泄漏目录描述符。如果我在应用程序的PID中使用lsof,它将显示一堆描述符,指向相同的几个目录,即它走过的那些目录。

private String getOldestFile() { 
    fileVisitor.clearOldestFile(); 

    try { 
     // FIXME: this was throwing FileSystemException: Too many open files after some time running. Leaking file descriptors!! 
     Files.walkFileTree(Paths.get(csvPath), fileVisitor); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 

    return fileVisitor.getOldestFile().toString(); 
} 

class CustomFileVisitor extends SimpleFileVisitor<Path> { 
    private Path oldestFile = null; 

    Path getOldestFile() { 
     return oldestFile; 
    } 

    void clearOldestFile() { 
     oldestFile = null; 
    } 

    @Override 
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 
     if (attrs.isDirectory()) 
      return FileVisitResult.CONTINUE; 

     if (oldestFile == null) 
      oldestFile = file; 

     if (oldestFile.compareTo(file) > 0) 
      oldestFile = file; 

     return FileVisitResult.CONTINUE; 
    } 

    @Override 
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { 
     if (dir.equals(Paths.get(csvPath))) 
      return FileVisitResult.CONTINUE; 

     if (Files.list(dir).collect(Collectors.toList()).size() == 0) 
      Files.delete(dir); // throws an exception if folder is not empty -> mustn't delete folder with files 

     return FileVisitResult.CONTINUE; 
    } 
} 

CustomFileVisitor仅创建一次,在外类和函数被定期调用等filename = getOldestFile();

编辑:过帐lsof -p {PID}输出。在开始时,我发现PID如this

这就是lsof -p {PID}输出的样子,只有成千上万行。 “/ home/leon/Development/data /”是Files.walkFileTree的输入。

java 14965 leon 285r  DIR    8,2  4096 1970798 /home/leon/Development/data/2017 
java 14965 leon 286r  DIR    8,2  4096 1970799 /home/leon/Development/data/2017/10 
java 14965 leon 287r  DIR    8,2  4096 1970799 /home/leon/Development/data/2017/10 
java 14965 leon 288r  DIR    8,2 36864 1970800 /home/leon/Development/data/2017/10/17 
java 14965 leon 289r  DIR    8,2 36864 1970800 /home/leon/Development/data/2017/10/17 
java 14965 leon 290r  DIR    8,2  4096 1970798 /home/leon/Development/data/2017 
java 14965 leon 291r  DIR    8,2  4096 1970798 /home/leon/Development/data/2017 
java 14965 leon 292r  DIR    8,2  4096 1970799 /home/leon/Development/data/2017/10 
java 14965 leon 293r  DIR    8,2  4096 1970799 /home/leon/Development/data/2017/10 
java 14965 leon 294r  DIR    8,2 36864 1970800 /home/leon/Development/data/2017/10/17 
java 14965 leon 295r  DIR    8,2 36864 1970800 /home/leon/Development/data/2017/10/17 

编辑2:我已经设法将问题隔离到这一行:Files.list(dir).collect(Collectors.toList()).size() == 0。这不应该被垃圾收集?

+0

这里没有打开任何文件的代码,为什么它会创建文件描述符?你可以发布'lsof'输出的片段吗? – Thomas

+0

@Thomas是的,我也不明白那一部分。如果我评论getOldestFile();行,根本没有泄漏。发布lsof输出片段。 – leonz

+0

啊,所以它不会打开文件的文件描述符,而是打开目录。你可以在你的问题中纠正这一点。 – Thomas

回答

4

Files#list() documentation

返回的数据流封装DirectoryStream。如果需要及时处理文件系统资源,则应使用try-with-resources构造来确保在流操作完成后调用流的close方法。

最终,流将被垃圾收集,但不会立即。所以你必须在这种情况下自己管理它。

+0

使用close()垃圾收集它应该。非常感谢。 – leonz