Правильно освобождаем ресурсы в Java
Неправильное решение №1
Данное решение опасно, потому что если в коде сгенерируется исключение, то stream.close() не будет вызван. Произойдет утечка ресурса (не закроется соединение, не будет освобожден файловый дескриптор и т.д.)
Неправильное решение №2
Попробуем исправить предыдущий код. Используем try-finally :
Теперь close() всегда будет вызываться (ибо finally ): ресурс в любом случае будет освобождён. Вроде всё правильно. Ведь так?
Неправильное решение №3
Попробуем исправить ситуацию. Если stream.close() может затереть «главное» исключение, то давайте просто «проглотим» исключение из close() :
Теперь вроде всё хорошо. Можем идти пить чай.
Как бы не так. Это решение ещё хуже предыдущего. Почему?
Если сравнить это решение с предыдущим, то там мы хотя бы узнаем, что произошло что-то плохое. Здесь же вся информация об ошибке пропадает.
Неидеальное решение
Итак, как же всё-таки правильно выглядит код обработки ресурса?
Правильное решение (Java 7)
Правильное решение (Java 6 с использованием Google Guava)
В Java 6 средствами одной лишь стандартной библиотеки не обойтись. Однако нам на помощь приходит замечательная библиотека Google Guava. В Guava 14.0 появился класс com.google.common.io.Closer ( try-with-resources для бедных), с помощью которого неидеальное решение выше можно заметно упростить:
Решение заметно длиннее, чем в случае Java 7, но всё же намного короче неидеального решения. Вывод будет примерно таким же, как Java 7.
Выводы
Правильно освобождать ресурсы не так просто, как кажется (просто только в Java 7). Всегда уделяйте этому должное внимание. InputStream и OutputStream ( Reader и Writer ) обрабатываются по-разному (по крайней мере в Java 6)!
Что такое подавленное исключение?
комментарий (от пользователя soc) на ответ вопрос об оптимизации хвостового вызова упоминалось, что Java 7 имеет новую функцию под названием «подавленные исключения», из-за» добавления ARM » (поддержка процессоров ARM?).
Что такое «подавленное исключение» в этом контексте? В других контекстах «подавленное исключение» было бы исключением, которое было поймано, а затем проигнорировано (редко хорошая идея); это явно что-то другое.
8 ответов:
Я считаю, что комментатор ссылается на исключение, которое наполовину игнорируется, когда оно выбрасывается в неявном finally блок a try-with-resources блок, в контексте существующего исключения из try блок:
исключение может быть выдано из блока кода, связанного с инструкцией try-with-resources. В Примере writeToFileZipFileContents исключение может быть вызвано из блока try и до двух исключения могут быть вызваны из инструкции try-with-resources при попытке закрыть объекты ZipFile и BufferedWriter. Если исключение создается из блока try и одно или несколько исключений создаются из оператора try-with-resources, то эти исключения, создаваемые из оператора try-with-resources, подавляются, а исключение, создаваемое блоком, является исключением, создаваемым методом writeToFileZipFileContents. Вы можете получить эти подавленные исключения, вызвав Метательный.метод getSuppressed из исключения, вызванного блоком try.
(это цитирует раздел под названием «подавленные исключения» из связанной страницы.)
компилятор должен определить, какой из них «действительно» бросить. Он выбирает, чтобы бросить исключение, вызванное в явном коде (код в try заблокировать) вместо того, чтобы тот, брошенный неявным кодом ( finally блок). Поэтому исключения, брошенные в неявном блоке, подавляются (игнорируются). Это происходит только в случае нескольких исключений.
перед Java7; есть исключения, брошенные в коде, но были проигнорированы как-то.
новый конструктор и два новых метода были добавлены в класс Throwable в JDK 7. Это, как показано ниже:
С помощью этого нового подхода мы также можем обрабатывать эти подавленные исключения.
в Java7 try-with-resources; исключение в AutoCloseable:: close() добавляется как подавленное исключение по умолчанию вместе с исключением попытки.
также известно, что это отличается от цепи исключений (были введены с JDK 1.4 и были предназначены, чтобы сделать возможным легко отслеживать причинно-следственные связи между исключениями.)
подавленных исключений являются дополнительными исключениями, которые происходят в инструкции try-with-resources (введено в Java 7) при AutoCloseable ресурсы закрыты. Потому что несколько исключений могут возникнуть при закрытии AutoCloseable ресурсы, дополнительные исключения прилагаются к первичное исключение как подавленные исключения.
глядя на байт-код фрагмента примера кода try-with-resources, standard JVM обработчики исключений используются для размещения семантики try-with-resources.
со всеми строками вы получите: java.lang.RuntimeException: from finally!
удаление finally блок вы получите: java.lang.RuntimeException: from catch!
удаление catch блок вы получите:
Я думаю, что это связано с «chained exception facility». Это повлияет на то, как исключение обрабатывается этим средством по мере развития трассировки стека. Со временем исключения, которые являются частью группы цепных исключений, могут быть подавлены. Посмотри на Throwable documentation для более подробной информации.
вы также можете подавлять исключения в Java 6 (немного обмана),
Я создал утилиту, которая прозрачно обрабатывает подавление исключения в Java 1.6 и Java 1.7. Вы можете найти реализацию здесь
все, что вам нужно, это позвонить:
подавить исключение, и
чтобы получить подавленные исключения исключения, если кто-то все еще использует Java 1.6
ARM-автоматическое управление ресурсами (введено с Java 7)
Одним из основных нововведений, которые были включены в Java 7, стало введение нового оператора «try c ресурсами». «try c ресурсами» это оператор try, в котором объявляются один или несколько ресурсов. Ресурс — это объект, который должен быть закрыт после того, как программа закончит с ним работу. «try c ресурсами» берет всю работу по закрытию ресурсов на себя. Прежде, чем подробно его рассмотреть давайте разберемся в причинах, которые вызвали его появление.
Закрытие ресурсов до Java 7.
Предположим нам нужно написать метод, который считывает первую строчку из одного файла и записывает ее в другой файл. Задача очень простая и ее реализация не должна вызвать каких-либо затруднений:
Слишком много кода для столь простой операции, не находите? Но и это еще не все, в коде выше есть существенный изъян, если при закрытии первого ресурса:
будет сгенерировано исключение, то второй ресурс закрыт не будет:
Самым очевидным выходом из данной ситуации – будет окружить закрытие ресурсов дополнительными блоками try:
Согласитесь, что выглядит ужасно, но до выхода Java 7 с этим приходилось мириться.
Использование try с ресурсами.
Появление оператора try с ресурсами значительно упростило жизнь программистам, больше не было необходимости громоздить конструкции try/catch в надежде, что ты закрыл все ресурсы и предусмотрел все возможные исключительные ситуации, большую часть этой работы try с ресурсами взял на себя. Рассмотрим его общий вид:
Главное отличие от привычного блока try в круглых скобках, в которых создаются ресурсы, которые впоследствии нужно закрыть, ресурсы будут закрываться снизу-вверх автоматически после завершения работы блока try, т.е. в примере, сначала закроется writer, а потом reader. Ресурсы созданные в блоке try(), в нашем случае reader и writer будут видны только в блоке try, в блоках catch и finally попытка их использования вызовет ошибку компиляции.
При использовании оператора try с ресурсами, совершенно не обязательно использовать блоки catch и finally, они являются опциональными, исходя из этого предыдущий пример можно переписать следующим образом:
Подобный код абсолютно легален и никаких ошибок компиляции не вызовет.
Настало время улучшить наш пример из начала статьи, который копирует первую строчку из одного файла в другой:
Получилось в два раза меньше кода, чем в первоначальном примере.
Интерфейс AutoCloseable.
Для того, чтобы класс можно было использовать в операторе try с ресурсами, он должен реализовывать интерфейс AutoCloseable. Хорошая новость, в том, что сделать это не так уж и сложно – необходимо реализовать всего лишь один метод – public void close() throws Exception.
Как и следовало ожидать сначала выполнился код из блока try, а потом произошло закрытие ресурса, с помощью переопределенного метода close(). Вы наверное заметили, что в примере, метод close() не генерирует и не объявляет никаких исключений, в отличии от метода объявленного в интерфейсе AutoCloseable – это вполне легально, переопределяемый метод может генерировать более конкретные исключения или не генерировать их вовсе. Еще на что хотелось бы обратить пристальное внимание, так это на саму реализацию метода close(). Метод close() не должен содержать никакой бизнес логики, только закрытие ресурсов:
Данный код конечно же скомпилируется, но закрытие ресурсов будет вызывать побочный эффект и изменять значение переменной, чего конечно же нужно избегать.
Подавленные исключения.
Подавленные исключения (suppressed exception) образуются, когда в блоке try генерируется исключение и в методе close() при закрытии ресурса генерируется исключение, в этом случае первое исключение считается главным остальные подавленные, чтобы было понятнее рассмотрим пример:
Вывод:
Main exception
Suppressed Exception
Suppressed Exception
В нашем случае получился следующий ход выполнения программы: сначала создаются два объекта Example ex1 и ex2, после чего происходит вход в блок try, где генерируется исключение, программа прекращает выполнения кода в блоке и приступает к закрытию ресурсов снизу-вверх. При закрытии каждого из ресурсов в методе close() генерируется еще по одному исключению, которые последовательно добавляются к главному исключению, сгенерированному в блоке try. После того, как ресурсы были закрыты, программа приступает к обработке исключения в блоке catch, где первой строчкой мы выводим информацию о главном исключении, а последующим циклом получаем подавленные исключения и выводим информацию о них.
Последнее на что стоит обратить внимание, так это, что подавленные исключения работают только в блоке try, в следующем примере будет сгенерировано два самостоятельных и независимых друг от друга исключения:
Что такое подавленное исключение?
комментарий (пользователем soc) на ответ to вопрос об оптимизации хвостового вызова упомянуто, что Java 7 имеет новую функцию, называемую «подавленными исключениями», из-за» добавления ARM » (поддержка процессоров ARM?).
Что такое «подавленное исключение» в этом контексте? В других контекстах «подавленное исключение» было бы исключением, которое было поймано, а затем проигнорировано (редко хорошая идея); это явно что-то отличающийся.
8 ответов
Я считаю, что комментатор ссылается на исключение, которое наполовину игнорируется, когда оно бросается в неявном finally блок try-with-resources блок, в контексте существующего исключения из try блок:
исключение может быть вызвано из блока кода, связанного с инструкцией try-with-resources. В Примере writeToFileZipFileContents из блока try может быть создано исключение и до двух исключения могут быть вызваны из инструкции try-with-resources при попытке закрыть объекты ZipFile и BufferedWriter. Если исключение из try блока и одного или нескольких исключений из try-оператор с ресурсами, то эти исключения из try-оператор с ресурсами подавляются, и исключение в блоке-это одно, что бросается writeToFileZipFileContents способ. Эти подавленные исключения можно получить, вызвав Перекидным.метод getSuppressed из исключения, создаваемого блоком try.
(это цитирование раздела под названием «подавленные исключения» со связанной страницы.)
компилятор должен определить, какой из них» действительно » бросить. Он решает выбросить исключение, вызванное в явном коде (код в try заблокировать) вместо того, чтобы бросать неявный код ( finally блок). Поэтому исключения, создаваемые в неявном блоке, подавляются (игнорируются). Это происходит только в случае нескольких исключений.
перед Java7; в коде есть исключения, но они были проигнорированы каким-то образом.
новый конструктор и два новых метода были добавлены в класс Throwable в JDK 7. Это, как показано ниже:
С этим новым подходом мы также можем справиться с этими подавленными исключениями.
В Java7 try-with-resources; исключение в AutoCloseable:: закрыть() по умолчанию добавляется как подавленное исключение вместе с исключением try.
также известно, что это отличается от цепи исключений (были введены с JDK 1.4 и были предназначены, чтобы сделать возможным легко отслеживать причинно-следственные связи между исключениями.)
подавленных исключений являются дополнительными исключениями, которые происходят в инструкции try-with-resources (введено в Java 7) при AutoCloseable ресурсы закрыты. Потому что при закрытии может произойти несколько исключений AutoCloseable ресурсы, дополнительные исключения прилагаются к первичное исключение как подавленные исключения.
глядя на байт-код фрагмента образца кода try-with-resources, standard в JVM обработчики исключений используются для размещения семантики try-with-resources.
Concedering код ниже:
со всеми линиями, вы получите: java.lang.RuntimeException: from finally!
удаление finally блок вы получите: java.lang.RuntimeException: from catch!
удаление catch блок вы получите:
Я думаю, что это связано с «прикованным средством исключения». Это повлияет на то, как исключение обрабатывается этим средством по мере развития трассировки стека. Со временем исключения, входящие в группу связанных исключений, могут быть подавлены. Посмотреть Throwable documentation для более подробной информации.
вы также можете подавлять исключения в Java 6 (немного обмана),
Я создал утилиту, которая прозрачно обрабатывает подавление исключений в Java 1.6 и Java 1.7. Вы можете найти реализацию здесь
все, что вам нужно, это позвонить:
чтобы подавить исключение, и
чтобы получить подавленные исключения исключения, в случае, если кто-то все еще использует Java 1.6
ARM-автоматическое управление ресурсами (введено с Java 7)
Что такое подавленное исключение?
Комментарий (пользователь soc) в ответ на вопрос об оптимизации хвостовых опций упоминал, что Java 7 имеет новую функцию, называемую «подавленные исключения», из-за «добавления ARM» (поддержка процессоров ARM?).
Что такое «исключенное исключение» в этом контексте? В других контекстах «исключенное исключение» было бы исключением, которое было поймано, а затем проигнорировано (редко хорошая идея); это явно что-то другое.
ОТВЕТЫ
Ответ 1
Я полагаю, что комментатор ссылается на это исключение, которое игнорируется, когда оно выбрано внутри неявного блока finally try-with-resources, в контексте того, что существующее исключение выбрано из блока try :
Исключение может быть выбрано из блока кода, связанного с оператором try-with-resources. В примере writeToFileZipFileContents исключение может быть выбрано из блока try, и до двух исключений может быть выбрано из инструкции try-with-resources при попытке закрыть объекты ZipFile и BufferedWriter. Если исключение выбрано из блока try, и одно или несколько исключений выбрасываются из оператора try-with-resources, то те исключения, которые выбраны из инструкции try-with-resources, подавляются, а исключение, созданное блоком, является тем, который вызывается методом writeToFileZipFileContents. Вы можете получить эти подавленные исключения, вызвав метод Throwable.getSuppressed из исключения, созданного блоком try.
(Это цитирует раздел «Подавленные исключения» на связанной странице.)
Ответ 2
Компилятор должен определить, какой из них «действительно» бросить. Он выбирает исключение, выраженное в явном коде (код в блоке try ), а не в том, что выбрано неявным кодом (блок finally ). Поэтому исключение (исключения), которое бросается в неявный блок, подавляется (игнорируется). Это происходит только в случае нескольких исключений.
Ответ 3
До Java7; В коде есть исключения, которые были проигнорированы.
В класс Throwable в JDK 7 был добавлен новый конструктор и два новых метода. Они приведены ниже:
с помощью этого нового подхода мы также справимся с этим исключенным исключением.
В Java7 try-with-resources; исключение в AutoCloseable:: close() добавляется как исключенное исключение по умолчанию вместе с исключением try.
Также известно, что это отличается от цепочки исключений (были введены с JDK 1.4 и были предназначены для того, чтобы можно было легко отслеживать причинно-следственные связи между исключениями.)
Ответ 4
Подавленные исключения являются дополнительными исключениями, которые происходят в инструкции try-with-resources (представленный в Java 7), когда AutoCloseable ресурсы закрыты. Поскольку при закрытии ресурсов AutoCloseable могут возникать множественные исключения, дополнительные исключения исключаются из в качестве исключенных исключений.
Ответ 5
Выполнение кода ниже:
Со всеми строками вы получите: java.lang.RuntimeException: from finally!
Удаление блока finally вы получите: java.lang.RuntimeException: from catch!
Удаление блока catch вы получите:
Ответ 6
Я думаю, что это связано с «цепным средством исключения». Это повлияет на то, каким образом исключение обрабатывается этим средством по мере развития трассировки стека. Со временем исключения, которые являются частью группы связанных исключений, могут быть подавлены. Подробнее см. Throwable documentation.
Ответ 7
Возьмем очень простой пример
Я собрал большинство возможных сценариев с фрагментами кода и выводами в следующем сообщении.
Надеюсь, что это поможет.
Ответ 8
Вы также можете подавлять исключения на Java 6 (вовлеченный небольшой обман),
Я создал утилиту, которая прозрачно обрабатывает исключение исключения в Java 1.6 и Java 1.7. Вы можете найти реализацию здесь
Все, что вам нужно, это позвонить:
чтобы исключить исключение, и
чтобы получить исключенные исключения из Exception, если кто-либо еще использует Java 1.6



