Слишком много открытых дескрипторов файлов

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

проблема, с которой я сейчас сталкиваюсь, заключается в том, что у нас заканчиваются дескрипторы файлов на нашем сервере Solaris. Я хотел бы знать, какой лучший способ отслеживать открытые дескрипторы файлов? Где посмотреть и что может привести к запуску открытых дескрипторов файлов?

Я не могу отлаживать приложение под Solaris, только в моей среде разработки Windows. IS-IS даже разумно анализировать открытые дескрипторы файлов под Windows?

12 ответов


одна хорошая вещь, которую я нашел для отслеживания незамкнутых дескрипторов файлов, - FindBugs:

http://findbugs.sourceforge.net/

Он проверяет много вещей, но одна из самых полезных-операции открытия/закрытия ресурсов. Это программа статического анализа, которая работает на вашем исходном коде, и она также доступна как плагин eclipse.


в windows вы можете посмотреть открытые дескрипторы файлов с помощью process explorer:

http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx

на Solaris вы можете использовать "lsof" для мониторинга открытых дескрипторов файлов


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


чтобы ответить на вторую часть вопроса:

что может вызвать запуск дескрипторов открытых файлов?

открытие большого количества файлов, очевидно, а затем не закрытие их.

самый простой сценарий заключается в том, что ссылки на любые объекты содержат собственные дескрипторы (например, FileInputStream) выбрасываются перед закрытием, что означает, что файлы остаются открытыми до завершения объектов.

другой вариант заключается в том, что объекты хранятся где-то и не закрыта. Свалка кучи может сказать вам, что задерживается где (jmap и jhat включены в JDK, или вы можете использовать jvisualvm если вы хотите GUI). Вы, вероятно, заинтересованы в поиске объектов, владеющих FileDescriptors.


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

#!/bin/bash
COUNTER=0
HOW_MANY=0
MAX=0
# do not take care about COUNTER - just flag, shown should we continie or not
while [ $COUNTER -lt 10 ]; do
    #run until process with passed pid alive
    if [ -r "/proc/" ]; then
        # count, how many files we have
        HOW_MANY=`/usr/sbin/lsof -p  | wc -l`
        #output for live monitoring
        echo `date +%H:%M:%S` $HOW_MANY
        # uncomment, if you want to save statistics
        #/usr/sbin/lsof -p  > ~/autocount/config_lsof_`echo $HOW_MANY`_`date +%H_%M_%S`.txt

        # look for max value
        if [ $MAX -lt $HOW_MANY ]; then
            let MAX=$HOW_MANY
            echo new max is $MAX
        fi 
        # test every second. if you don`t need so frequenlty test - increase this value
        sleep 1
    else
        echo max count is $MAX
        echo Process was finished
        let COUNTER=11
    fi
done

также вы можете попробовать играть с JVM ontion-Xverify: none - он должен отключить проверку jar (если большинство открытых файлов-jars...). Для утечек через не закрытый FileOutputStream вы можете использовать findbug (наставник выше) или попытаться найти статью Как исправить стандартный Java FileOutputStream/FileInputStream , где можно увидеть, кто открывает файлы, а кто забыл их закрыть. К сожалению, не могу найти эту статью прямо сейчас, но она существует :) Также подумайте об увеличении filelimit - для современных ядер * Nix не проблема обрабатывать более 1024 fd.


Это может быть непрактично в вашем случае, но то, что я сделал однажды, когда у меня была аналогичная проблема с открытыми подключениями к базе данных, переопределило функцию "open" с моей собственной. (Удобно, что у меня уже была эта функция, потому что мы написали наш собственный пул соединений.) В моей функции я добавил запись в таблицу запись открыта. Я сделал вызов трассировки стека и сохранил идентификацию вызывающего абонента, а также время вызова, и я забыл, что еще. Когда соединение было освобождено, я удалил элемент таблицы. Затем у меня был экран, где мы могли бы сбросить список открытых записей. Затем вы могли посмотреть на отметку времени и легко увидеть, какие соединения были открыты в течение маловероятного количества времени, и какие функции сделали эти открытия.

из этого мы смогли быстро отследить пару функций, которые открывали соединения и не закрывали их.

Если у вас есть много открытых дескрипторов файлов, вероятность того, что вы не сможете закрыть их, когда вы сделано где-то. Вы говорите, что проверили правильность блоков try/finally, но я подозреваю, что где-то в коде вы либо пропустили плохой, либо у вас есть функция, которая передает и никогда не доходит до finally. Я полагаю, также возможно, что вы действительно делаете правильные закрытия каждый раз, когда открываете файл, но вы открываете сотни файлов одновременно. Если это так, я не уверен, что вы можете сделать, кроме серьезного редизайна программы для управления меньшим количеством файлов или серьезной программы редизайн для очереди доступа к файлам. (В этот момент я добавляю обычное: "не зная подробностей вашего заявления и т. д.)


Я бы начал с запроса моего sysadmin, чтобы получить список всех открытых файловых дескрипторов для процесса. Разные системы делают это по-разному: Linux, например, имеет . Я помню, что у Solaris есть команда (возможно pfiles?) это сделает то же самое-ваш сисадмин должен знать это.

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


Не прямой ответ на ваш вопрос, но эти проблемы могут быть результатом неправильно выпуская файл ресурсов в код. Например, если вы работаете с классами FileOutputsStream, убедитесь, что методы close вызываются в блоке finally, как в этом примере:

FileOutputsStream out = null;
try {
  //You're file handling code
} catch (IOException e) {
  //Handle
} finally {
  if (out != null) {
    try { out.close(): } catch (IOException e) { }
  }
}

Я бы дважды проверил настройки среды на вашем поле Solaris. Я считаю, что по умолчанию Solaris разрешает только 256 дескрипторов файлов для каждого процесса. Для серверного приложения, особенно если оно работает на выделенном сервере, это очень низко. Рисунок 50 или более дескрипторов для открытия JRE и библиотечных банок, а затем по крайней мере один дескриптор для каждого входящего запроса и запроса базы данных, вероятно, больше, и вы можете видеть, как это просто не сократит горчицу для серьезного сервер.

посмотреть /etc/system file, для значений rlim_fd_cur и rlim_fd_max, чтобы увидеть, что ваша система имеет набор. Затем подумайте, разумно ли это (вы можете увидеть, сколько файловых дескрипторов открыто, пока сервер работает с lsof команда, в идеале с параметром-p [process ID].


Это, безусловно, может дать вам идею. Поскольку это Java, механика открытия/закрытия файла должна быть реализована аналогично (если только одна из JVMs не реализована неправильно). Я бы рекомендовал использовать Файловый Монитор на Windows.


Google для приложения под названием filemon из внутренних систем.

кстати, чтобы отследить это, вы можете использовать что-то вроде aspectj для регистрации всех вызовов, которые открывают и закрывают файлы и регистрируют, где они происходят.


это шаблон кодирования, который помогает найти незакрытые ресурсы. Он закрывает ресурсы, а также жалуется в журнале на проблему.

class
{
    boolean closed = false;
    File file;

    close() {
        closed = true;
        file.close();
    }

    finalize() {
        if (!closed) {
            log error "OI! YOU FORGOT TO CLOSE A FILE!"
        file.close();
    }
}

оберните вышеуказанный файл.close () вызывает блоки try-catch, которые игнорируют ошибки.

кроме того, Java 7 имеет новую функцию "try-with-resource", которая может автоматически закрывать ресурсы.