Java IOException "Слишком много открытых файлов" - PullRequest
43 голосов
/ 27 ноября 2010

Я делаю несколько файловых операций ввода / вывода с несколькими файлами (запись в 19 файлов, так бывает).После того, как я написал им несколько сотен раз, я получаю Java IOException: Too many open files.Но на самом деле у меня есть только несколько файлов, открытых одновременно.В чем здесь проблема?Я могу убедиться, что запись прошла успешно.

Ответы [ 5 ]

52 голосов
/ 27 ноября 2010

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

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

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

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

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

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 - Зашито, как скомпилировано в ядро. Изменение количества доступных слотов 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», которые будут подключены к стандартному вводу / выводу / ошибке внешнего приложения.

3 голосов
/ 25 сентября 2014

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

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

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

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

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

Надеюсь, это кому-нибудь поможет.

2 голосов
/ 05 декабря 2013

Для UNIX:

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

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

   $ ulimit -n

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

   $ ulimit -n <value>

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

2 голосов
/ 27 ноября 2010

Вы явно не закрываете свои файловые дескрипторы перед открытием новых.Вы на Windows или Linux?

0 голосов
/ 19 января 2012

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

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

int index;
while () {
    try {
        // do with outputStream...
    } finally {
        out.close();
    }
    if (index++ % 100 = 0)
        System.gc();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...