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

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

Нет ничего плохого в расширении Throwable, чтобы иметь возможность генерировать (и обрабатывать) пользовательские исключения. Тем не менее, вы должны помнить следующее:

  • Использование наиболее конкретного возможного исключения - отличная идея. Это позволит вызывающей стороне по-разному обрабатывать различные типы исключений. Важно помнить об иерархии классов исключения, поэтому пользовательское исключение должно расширять другой тип Throwable, максимально приближенный к вашему исключению.
  • Расширение Throwable может быть слишком высокого уровня. Попробуйте вместо этого расширить Exception или RuntimeException (или исключение более низкого уровня, которое ближе к причине, по которой вы генерируете исключение). Имейте в виду разницу между RuntimeException и Exception.
  • Вызов метода, который генерирует Exception (или подкласс Exception), должен быть заключен в блок try / catch, который способен обрабатывать исключение. Это хорошо для случаев, когда вы ожидаете, что что-то пойдет не так, из-за обстоятельств, которые могут быть вне вашего контроля (например, из-за сбоя сети).
  • Вызов метода, который генерирует RuntimeException (или его подкласс), не нужно заключать в блок try / catch, который может обработать исключение. (Это, конечно, может быть, но это не обязательно.) Это больше для исключений, которые действительно не следует ожидать.

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

public class Pig extends Throwable { ... }
public class FlyingPig extends Pig { ... }
public class Swine extends Pig { ... }
public class CustomRuntimeException extends RuntimeException { ... }

И некоторые методы

public void foo() throws Pig { ... }
public void bar() throws FlyingPig, Swine { ... }
// suppose this next method could throw a CustomRuntimeException, but it
// doesn't need to be declared, since CustomRuntimeException is a subclass
// of RuntimeException
public void baz() { ... } 

Теперь у вас может быть некоторый код, который вызывает эти методы похожи на это:

try {
    foo();
} catch (Pig e) {
    ...
}

try {
    bar();
} catch (Pig e) {
    ...
}

baz();

Обратите внимание, что когда мы вызываем bar(), мы можем просто поймать Pig, так как FlyingPig и Swine расширяются Pig. Это полезно, если вы хотите сделать то же самое для обработки любого исключения. Вы могли бы, однако, обращаться с ними по-другому:

try {
    bar();
} catch (FlyingPig e) {
    ...
} catch (Swine e) {
    ...
}
9
задан dlinsin 2 November 2009 в 13:24
поделиться

11 ответов

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

http: //findbugs.sourceforge.net/

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

8
ответ дан 4 December 2019 в 08:15
поделиться

В Windows вы можете посмотреть дескрипторы открытых файлов с помощью проводника процессов:

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

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

7
ответ дан 4 December 2019 в 08:15
поделиться

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

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

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

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

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

2
ответ дан 4 December 2019 в 08:15
поделиться

Этот небольшой скрипт помогает мне следить за количеством открытых файлов, когда мне нужно проверить количество микросхем. Если использовался в 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/$1" ]; then
        # count, how many files we have
        HOW_MANY=`/usr/sbin/lsof -p $1 | wc -l`
        #output for live monitoring
        echo `date +%H:%M:%S` $HOW_MANY
        # uncomment, if you want to save statistics
        #/usr/sbin/lsof -p $1 > ~/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 (если большинство открытых файлов - это jar-файлы. ..). Для утечек через незакрытый FileOutputStream вы можете использовать findbug (описанный выше) или попытаться найти статью, как исправить стандартный java FileOutputStream / FileInputStream, где вы можете увидеть, кто открывал файлы и забыл их закрыть. К сожалению, сейчас не могу найти эту статью, но она существует :) Также подумайте об увеличении ограничения файла - для современных ядер * nix проблема не будет превышать 1024 fd.

2
ответ дан 4 December 2019 в 08:15
поделиться

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

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

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

2
ответ дан 4 December 2019 в 08:15
поделиться

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

2
ответ дан 4 December 2019 в 08:15
поделиться

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

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

1
ответ дан 4 December 2019 в 08:15
поделиться

Нет прямого ответа на ваш вопрос, но эти проблемы могут быть результатом неправильного освобождения файловых ресурсов в вашем устаревшем коде. Например, если вы работаете с классами 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) { }
  }
}
1
ответ дан 4 December 2019 в 08:15
поделиться

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

Взгляните на файл / etc / system , чтобы найти значения rlim_fd_cur и rlim_fd_max , чтобы увидеть, что ваша система установила . Затем подумайте, разумно ли это (вы можете увидеть, сколько файловых дескрипторов открыто во время работы сервера, с помощью команды lsof ,

1
ответ дан 4 December 2019 в 08:15
поделиться

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

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

0
ответ дан 4 December 2019 в 08:15
поделиться

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

0
ответ дан 4 December 2019 в 08:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: