Files.list
やFiles.walk
やFiles.find
の戻り値であるStream<Path>
はDirectoryStream
をラップしたもので、利用後は必ずclose
する必要があります。このことはJavaDocにも以下の通りに記述されています。
返されるストリームは、1つ以上のDirectoryStreamをカプセル化します。ファイル・システム・リソースのタイムリな破棄が必要な場合は、try-with-resources構文を使用して、ストリーム操作の完了後にストリームのcloseメソッドが呼び出されるようにしてください。クローズされたストリームに対して操作すると、結果としてIllegalStateExceptionが発生します。
ではDirecotryStream
のラッパーであるStream<Path>
をclose
しない場合、どうなるでしょうか? 以下のコードはFiles.list
をclose
せずに呼び出し続けるものです。
public class Main {
public static void main(String[] args) throws IOException {
while (true) {
Files.list(Paths.get("/dev")).collect(Collectors.toList());
}
}
}
これを実行してみると、以下のような例外が発生します。
Exception in thread "main" java.nio.file.FileSystemException: /dev: Too many open files
at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:100)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:116)
at java.base/sun.nio.fs.UnixFileSystemProvider.newDirectoryStream(UnixFileSystemProvider.java:432)
at java.base/java.nio.file.Files.newDirectoryStream(Files.java:471)
at java.base/java.nio.file.Files.list(Files.java:3698)
at list.Main.main(Main.java:11)
Too many open files
というメッセージからもわかる通り、Stream<Path>
を明示的にクローズしないと、ファイルディスクリプタがオープンしたままになって、そのうちにプロセスのファイルディスクリプタの上限に達してしまいます。
よってJavaDocに書かれてある通り、Stream<Path>
は使い終わったら、明確にクローズする必要があります。
public class Main {
public static void main(String[] args) {
try (Stream<Path> ds = Files.list(Paths.get("/dev"))) {
List<Path> paths = ds.collect(Collectors.toList());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
環境情報
C:\>javac -version
javac 11.0.3
C:\>java -version
openjdk version "11.0.3" 2019-04-16
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.3+7)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.3+7, mixed mode)