Подтвердить что ты не робот

Java слишком много открытых файлов

Я пытаюсь записать несколько файлов, 19, если быть точным. После написания им несколько сотен раз я получаю Java IOException: Слишком много открытых файлов. Но, как я уже сказал, у меня ровно 19 файлов, и я открыл их в начале. В чем проблема? Я могу проверить, что записи были успешными.

изменить: Я не использовал блок try-catch-finally. У меня были функции вместо исключения. Теперь, когда я положил try-catch, наконец, вокруг них, похоже, они делают лучше.

Большинство из вас были правы в том, что я открываю больше файлов, чем я думал. Все еще отслеживает ситуацию. Я опубликую обновление после бит.

повторное редактирование: обеспечив, чтобы весь доступ к файлам был завершен с помощью try-catch-finally, исправил проблему. Благодаря

4b9b3361

Ответ 1

В Linux и других UNIX/UNIX-подобных платформах ОС ограничивает количество дескрипторов открытых файлов, которые могут иметь процессы в любой момент времени. В прежние времена этот предел был жестко связан 1 и относительно мал. В наши дни он намного больше (сотни/тысячи) и зависит от "мягкого" настраиваемого ресурса для каждого процесса. (Посмотрите на встроенную оболочку ulimit...)

Ваше приложение Java должно превышать ограничение дескриптора файла для каждого процесса.

Вы говорите, что у вас открыто 19 файлов, и через несколько сотен раз вы получите сообщение IOException, в котором говорится: "слишком много файлов открыто". Теперь это конкретное исключение может ТОЛЬКО случаться, когда запрашивается новый файловый дескриптор; т.е. когда вы открываете файл (или трубу или сокет). Вы можете проверить это, распечатав stacktrace для исключения IOException.

Если ваше приложение не запущено с небольшим лимитом ресурсов (что кажется маловероятным), следует, что он должен многократно открывать файлы/сокеты/каналы и не закрывать их. Узнайте, почему это происходит, и вы должны понять, что с этим делать.

FYI, следующий шаблон - безопасный способ записи в файлы, которые гарантируют, что не будут терять файловые дескрипторы.

Writer w = new FileWriter(...);
try {
    // write stuff to the file
} finally {
    try {
        w.close();
    } catch (IOException ex) {
        // Log error writing file and bail out.
    }
}

1 - Hardwired, как в компиляции в ядро. Изменение количества доступных слотов fd потребовало перекомпиляции... и может привести к тому, что меньше памяти будет доступно для других целей. В то время, когда Unix обычно работала на 16-битных машинах, все это имело значение.

UPDATE

Путь Java 7 более краткий:

try (Writer w = new FileWriter(...)) {
    // write stuff to the file
} // the `w` resource is automatically closed 

ОБНОВЛЕНИЕ 2

По-видимому, вы также можете столкнуться с "слишком большим количеством файлов" при попытке запустить внешнюю программу. Основная причина такая, как описано выше. Однако причина, по которой вы сталкиваетесь с этим в exec(...), заключается в том, что JVM пытается создать дескрипторы файлов "pipe", которые будут подключены к стандартным входам/выводам/ошибкам внешнего приложения.

Ответ 2

Вы, очевидно, не закрываете дескрипторы файлов, прежде чем открывать новые. Вы на окнах или Linux?

Ответ 3

Для UNIX:

Как предложил Стивен С, изменение максимального значения дескриптора файла до более высокого значения позволяет избежать этой проблемы.

Попробуйте взглянуть на ваш существующий файловый дескриптор:

   $ ulimit -n

Затем измените лимит в соответствии с вашими требованиями.

   $ ulimit -n <value>

Обратите внимание, что это просто изменяет пределы в текущей оболочке и любом процессе child/descendant. Чтобы изменить "stick", вам нужно поместить его в соответствующий сценарий оболочки или файл инициализации.

Ответ 4

Хотя в большинстве случаев ошибка явно очевидна, что дескрипторы файлов не были закрыты, я просто столкнулся с экземпляром JDK7 на Linux, который хорошо... достаточно подробно объяснил здесь.

Программа открыла FileOutputStream (fos), BufferedOutputStream (bos) и DataOutputStream (dos). После записи в dataoutstream, dos был закрыт, и я подумал, что все пошло нормально.

Внутри, однако, dos, попытался очистить bos, который вернул ошибку полного диска. Это исключение было съедено DataOutputStream, и, как следствие, основной бос не был закрыт, поэтому fos все еще был открыт.

На более позднем этапе этот файл был переименован из (что-то с .tmp) в его настоящее имя. Таким образом, дескрипторы дескриптора java файлов потеряли исходный .tmp, но он все еще был открыт!

Чтобы решить эту проблему, я должен был сначала сначала очистить DataOutputStream, получить IOException и закрыть сам FileOutputStream.

Я надеюсь, что это поможет кому-то.

Ответ 5

Недавно у меня были файлы пакетной обработки программ, я, конечно, закрыл каждый файл в цикле, но ошибка все еще там.

И позже я решил эту проблему с помощью мусора собирать с нетерпением каждые сотни файлов:

int index;
while () {
    try {
        // do with outputStream...
    } finally {
        out.close();
    }
    if (index++ % 100 = 0)
        System.gc();
}