私のアプリに奇妙なバグがありました。私はすでに回避策で解決しましたが、なぜこのバグが起きているのか不思議です。Files.walkFileTreeがカスタム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();
EDITのように定期的に呼び出されます。lsof -p {PID}
出力を投稿します。最初はthisのような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
EDIT 2:私は問題をこの行に隔離することができました:Files.list(dir).collect(Collectors.toList()).size() == 0
。これはガベージコレクションではないはずですか?
ファイルを開くコードはありません。なぜファイル記述子を作成するのですか? 'lsof'出力のスニペットを投稿できますか? – Thomas
@トーマスええ、私もその部分を理解していません。私がgetOldestFile()をコメントした場合;ライン、全くリークはありません。 lsof出力スニペットを投稿しました。 – leonz
ああ、それはファイルのファイル記述子を開くのではなく、ディレクトリのためです。あなたはあなたの質問にそれを修正することができます。 – Thomas