Итерирование файлов в scala/java в O (1) дескрипторы открытых файлов

Похоже, что nio .list возвращает поток, который при потреблении удерживается в одном файловом дескрипторе на каждый итерационный файл, до тех пор, пока .close не будет вызван во весь поток. Это означает, что каталоги данных с более чем 1000 файлами могут легко обрезать общие значения ulimit. Общий эффект от накопления дескриптора файла, еще более усугубляется при работе с вложенными обходами.

Что может быть альтернативным способом перебора файлов больших каталогов, кроме перехода на вызовы нереста к команде списка файлов ОС? Было бы здорово, если бы итерации файлов большого каталога, файловый дескриптор поддерживался бы только за текущий итерированный файл, что подразумевалось в правильной семантике потока.

Edit:

list возвращает поток java из java.nio.file.Path Какой вызов api будет использоваться для закрытия каждого элемента в потоке после его обработки, а не только при закрытии всего потока для более быстрой итерации? В scala это можно легко запустить с помощью api-оболочки из лучших файлов, в результате чего здесь.

Ответы

Ответ 1

Я столкнулся с той же проблемой (в Windows Server 2012 R2), когда я не закрыл поток. Все файлы, которые я повторил, были открыты в режиме чтения, пока JVM не был отключен. Однако это не произошло в Mac OS X, и поскольку поток зависит от OS-зависимых реализаций FileSystemProvider и DirectoryStream, я предполагаю, что проблема также может быть зависимой от ОС.

В отличие от комментария @Ian McLaird, он упоминается в Files.list() документации, которая

Если требуется своевременное удаление ресурсов файловой системы, следует использовать конструкцию try-with-resources для обеспечения того, чтобы метод закрытия потока был вызван после завершения операций потока.

Возвращенный поток - это DirectoryStream, чей Javadoc говорит:

DirectoryStream открывается при создании и закрывается вызовом метода close. Закрытие потока каталога освобождает любые ресурсы, связанные с потоком. Невозможность закрыть поток может привести к утечке ресурса.

Мое решение состояло в том, чтобы следовать совету и использовать конструкцию try-with-resources

try (Stream<Path> fileListing = Files.list(directoryPath)) {
    // use the fileListing stream
}

Когда я правильно закрыл поток (использовал выше конструкцию try-with-resources), дескрипторы файлов были немедленно выпущены.

Если вы не хотите получать файлы в виде потока, или вы в порядке с загрузкой всего списка файлов в память и преобразованием его в поток самостоятельно, вы можете использовать IO API:

File directory = new File("/path/to/dir");
File[] files = directory.listFiles();
if (files != null) { // 'files' can be null if 'directory' "does not denote a directory, or if an I/O error occurs."
    // use the 'files' array or convert to a stream:
    Stream<File> fileStream = Arrays.stream(files);
}

У меня не было проблем с блокировкой файлов с этим. Однако обратите внимание, что оба решения зависят от собственного, зависящего от ОС кода, поэтому я советую тестировать во всех средах, которые вы использовали.

Ответ 2

Если это происходит, почему бы не использовать старую школу java.io.File?

File folder = new File(pathToFolder);
String[] files = folder.list();

тестируется с помощью lsof, и похоже, что ни один из перечисленных файлов не открыт. После этого вы можете преобразовать массив в список или поток. Если каталог слишком большой или удаленный, тогда я попытаюсь обвинить объекты Path и сбор мусора или как-то их уничтожить.

Ответ 3

Вы можете использовать библиотеку Apache FileUtils, в которой используется старая Функция java.io.File.listFiles internaly:

Iterator<File> it = FileUtils.iterateFiles(folder, null, true);
while (it.hasNext())
{
   File fileEntry = (File) it.next();
}