При каких обстоятельствах блок finally {} НЕ будет выполнен?

На Яве try{} ... catch{} ... finally{} блок, код внутри finally{} обычно считается "гарантированным", чтобы работать независимо от того, что происходит в try/catch. Однако я знаю как минимум два обстоятельства, при которых он не будет выполняться:

  • Если System.exit(0) называется; или же,
  • если исключение выдается полностью до JVM, и происходит поведение по умолчанию (т.е. printStackTrace() и выход)

Существуют ли какие-либо другие программные поведения, которые будут препятствовать коду в finally{} заблокировать от выполнения? При каких конкретных условиях будет выполняться код или нет?

РЕДАКТИРОВАТЬ: Как указывает NullUserException, второй случай на самом деле не соответствует действительности. Я думал, что это потому, что текст в стандартной ошибке печатается после этого в стандартном выводе, предотвращая просмотр текста без прокрутки вверх.:) Извиняюсь.

9 ответов

Решение

Если вы позвоните System.exit() программа выходит сразу без finally будучи призванным

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

Бесконечный цикл также предотвратит окончательный вызов.

Блок finally всегда вызывается, когда выбрасывается Throwable. Даже если вы вызываете Thread.stop(), который вызывает ThreadDeath быть брошенным в целевой поток. Это можно поймать (это Error) и блок finally будет вызван.


public static void main(String[] args) {
    testOutOfMemoryError();
    testThreadInterrupted();
    testThreadStop();
    testStackru();
}

private static void testThreadStop() {
    try {
        try {
            final Thread thread = Thread.currentThread();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    thread.stop();
                }
            }).start();
            while(true)
                Thread.sleep(1000);
        } finally {
            System.out.print("finally called after ");
        }
    } catch (Throwable t) {
        System.out.println(t);
    }
}

private static void testThreadInterrupted() {
    try {
        try {
            final Thread thread = Thread.currentThread();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    thread.interrupt();
                }
            }).start();
            while(true)
                Thread.sleep(1000);
        } finally {
            System.out.print("finally called after ");
        }
    } catch (Throwable t) {
        System.out.println(t);
    }
}

private static void testOutOfMemoryError() {
    try {
        try {
            List<byte[]> bytes = new ArrayList<byte[]>();
            while(true)
                bytes.add(new byte[8*1024*1024]);
        } finally {
            System.out.print("finally called after ");
        }
    } catch (Throwable t) {
        System.out.println(t);
    }
}

private static void testStackru() {
    try {
        try {
            testStackru0();
        } finally {
            System.out.print("finally called after ");
        }
    } catch (Throwable t) {
        System.out.println(t);
    }
}

private static void testStackru0() {
    testStackru0();
}

печать

finally called after java.lang.OutOfMemoryError: Java heap space
finally called after java.lang.InterruptedException: sleep interrupted
finally called after java.lang.ThreadDeath
finally called after java.lang.StackruError

Примечание: в каждом случае поток продолжал работать, даже после SO, OOME, Interrupted и Thread.stop()!

Бесконечный цикл в try блок.

Поврежденная RAM? Программа больше не работает как написано? Я на самом деле отладил это однажды на машине DOS.

Тестирование блока finally в другом операторе в блоке try.

 public static void main(String [] args){

    try{
        System.out.println("Before Statement");
        /*** Statement ***/
        System.out.println("After Statement");
    }
    catch(Exception e){
    }
    finally{
        System.out.println("Finally is Executed");
    }

Заявления, в которых выполняется блок finally:

  1. Thread.currentThread().interrupted();
  2. Thread.currentThread().destroy();
  3. Thread.currentThread().stop();
  4. Thread.sleep(10);
  5. Thread.currentThread().interrupt();
  6. Runtime.getRuntime().addShutdownHook(Thread.currentThread());
  7. Если произошло какое-либо исключение.
  8. Если нет исключения.

Заявления, в которых блок finally не выполняется, следующие:

  1. Thread.currentThread().suspend();
  2. System.exit(0);
  3. JVM разбился.
  4. Питание чипа процессора отключается.
  5. ОС убивает процесс JVM.
  6. Runtime.getRuntime().exit(0);
  7. Runtime.getRuntime().halt(0);

Можно было бы сказать: "Наконец, это часть потока daeomon, который не может быть выполнен".

Существует вероятность частичного выполнения, когда, наконец, само выдает исключение (или приводит к ошибке)

Единственные разы, когда окончательно не будут названы:

если питание отключается

  1. если вы вызываете System.exit()
  2. если JVM падает в первую очередь
  3. если есть бесконечный цикл в блоке try
  4. если питание отключается

Другой возможный экземпляр блока finally, который никогда не выполняется, может быть вызван конструкцией, в которой метод возвращается до того, как был введен блок try, как, например, в некоторых случаях очень плохого кода, который я видел время от времени:

public ObjectOfSomeType getMeAnObjectOfSomeType() throws SomeHorrendousException {
    if (checkSomeObjectState()) {
        return new ObjectOfSomeType();
    }

    try {
        // yada yada yada...
    } catch (SomeHorrendousException shexc) {
        // wow, do something about this horrendous exception...
    } finally {
        // do some really important cleanup and state invalidation stuff...
    }

Я знаю, что никто из вас никогда этого не сделает, поэтому я не решался добавить это в качестве возможного сценария, но подумал: а, ну что, пятница, какого черта;)

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

Вы можете сделать его частью Daemon Thread. Вы можете использовать метод setDaemon(boolean status) который используется для пометки текущего потока как потока демона или пользовательского потока и выхода из JVM по мере необходимости. Это позволит вам выйти из JVM до finally{} выполнен.

Другие вопросы по тегам