2017-10-18 69 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