Läuft immer ein finally-Block?

112

Gibt es eine Bedingung, unter der möglicherweise nicht in Java ausgeführt wird? Vielen Dank.

Krieger
quelle
13
Ist dies eine Frage, die Ihnen möglicherweise gestellt wird, wenn Sie versuchen, einen Job bei einem bestimmten bekannten Unternehmen zu finden?
Tom Hawtin - Tackline
@ TomHawtin-Tackline Möchtest du es nennen? (Gott, ich habe vermisst, wie alt dieser Beitrag war!)
Hele
6
@Hele Ich würde das Spiel nicht verschenken wollen, aber du kannst es googeln.
Tom Hawtin - Tackline
Kurzantwort: Ja, unter normalen Bedingungen.
Shark

Antworten:

139

aus den Sun Tutorials

Hinweis: Wenn die JVM beendet wird, während der Try- oder Catch-Code ausgeführt wird, wird der finally-Block möglicherweise nicht ausgeführt. Wenn der Thread, der den Try- oder Catch-Code ausführt, unterbrochen oder beendet wird, wird der finally-Block möglicherweise nicht ausgeführt, obwohl die gesamte Anwendung fortgesetzt wird.

Ich kenne keine anderen Möglichkeiten, wie der finally-Block nicht ausgeführt werden würde ...

hhafez
quelle
6
@dhiller - Ich bin mir ziemlich sicher, dass "Power Down" in "Wenn die JVM beendet wird ..." enthalten ist :-p
Jason Coco
2
@ Jason Coco: Beenden (wie bei Stromausfall) ist nicht ganz dasselbe wie Beenden; Letzteres ist ein mehr oder weniger organisierter Prozess, der in Ersterem gipfelt. ; p
user359996
2
AFAIK, wenn ein Thread unterbrochen wird, wird er nicht sofort angehalten. Es liegt am Code im Thread, eine Unterbrechung zu erkennen und die Aufgabe zu stoppen. Schließlich sollte der Code ausgeführt werden.
Bart van Heukelom
2
Ich denke, wenn eine Ausnahme im finally-Block ausgelöst wird, wird der Rest des Blocks nicht ausgeführt.
Adriaan Koster
Nun, das ist scheiße!
Eiran
63

System.exit fährt die virtuelle Maschine herunter.

Beendet die aktuell ausgeführte Java Virtual Machine. Das Argument dient als Statuscode. Gemäß der Konvention zeigt ein Statuscode ungleich Null eine abnormale Beendigung an.

Diese Methode ruft die exitMethode in der Klasse auf Runtime. Diese Methode wird nie normal zurückgegeben.

    try {
        System.out.println("hello");
        System.exit(0);
    }
    finally {
        System.out.println("bye");
    } // try-finally

"bye" wird im obigen Code nicht ausgedruckt.

Eugene Yokota
quelle
Außerdem sollte eine Ausnahme die Java Virtual Machine herunterfahren.
Kaissun
3
Wenn beim Ausführen von System.exit (0) eine Ausnahme auftritt, wird der Block "finally" ausgeführt.
Halil
50

Nur um das zu erweitern, was andere gesagt haben, wird alles, was nicht dazu führt, dass die JVM beendet wird, den endgültigen Block verursachen. Also die folgende Methode:

public static int Stupid() {
  try {
    return 0;
  }
  finally {
    return 1;
  }
}

wird seltsamerweise sowohl kompilieren als auch 1 zurückgeben.

Daniel Nadasi
quelle
2
Das hat mich vor ein paar Wochen für ein paar Stunden wirklich verwirrt.
Nickf
3
Es wird als schlechte Idee angesehen, einen Wert aus einem finally-Block zurückzugeben. Entweder nur vom try-Block zurückkehren oder von außerhalb des try / finally-Blocks zurückkehren. Die meisten IDEs markieren dies mit einer Warnung.
Ran Biron
1
@nickf Ich verstehe, dass du nicht mehr verwirrt bist. Könnten Sie näher erläutern, warum 1 und nicht 0 zurückgegeben wird? Ich konnte nur vermuten, dass der Speicher (oder ein Register), in dem der Rückgabewert der Funktion gespeichert ist, die anfänglich 0 enthält, beim Ausführen des finally-Blocks überschrieben wird .
Yaneeve
2
Das ist merkwürdig, in C # darf es nicht von einem finally-Block zurückkehren.
JMCF125
3
@ RanBiron Natürlich. Er empfahl eigentlich nicht, innerhalb eines finally-Blocks zurückzukehren, sondern versuchte nur zu demonstrieren, dass selbst eine return-Anweisung weiterhin dazu führt, dass Code in diesem Block ausgeführt wird.
Aquarelle
15

Im Zusammenhang mit System.exit gibt es auch bestimmte Arten von katastrophalen Fehlern, bei denen ein finally-Block möglicherweise nicht ausgeführt wird. Wenn der JVM nicht genügend Speicherplatz zur Verfügung steht, wird sie möglicherweise einfach beendet, ohne dass sie abgefangen wird oder endgültig passiert.

Insbesondere erinnere ich mich an ein Projekt, bei dem wir dummerweise versucht haben, es zu verwenden

catch (OutOfMemoryError oome) {
    // do stuff
}

Dies funktionierte nicht, da die JVM keinen Speicher mehr zum Ausführen des catch-Blocks hatte.

Zarkonnen
quelle
Wenn OutOfMemoryError ausgelöst wird, bleibt normalerweise viel Speicher übrig (um das GC-Thrashing zu stoppen). Wenn Sie es jedoch wiederholt fangen, kehren Sie offensichtlich zum GC-Thrashing zurück.
Tom Hawtin - Tackline
Ich dachte, man sollte keine ungeprüften Ausnahmen fangen!
Sergii Shevchyk
1
Ich habe es auf meiner Seite mit jdk7 versucht, aber es hat den OutOfMemory-Fehler abgefangen!
Jaskey
10
try { for (;;); } finally { System.err.println("?"); }

In diesem Fall wird die Funktion "finally" nicht ausgeführt (es sei denn, die veraltete Funktion Thread.stopwird aufgerufen oder eine entsprechende Funktion, z. B. über eine Tool-Schnittstelle).

Tom Hawtin - Tackline
quelle
Diese Seite behauptet, dass ein ThreadDeath-Fehler ausgelöst wird und dass sich der Stapel normal auflöst, wenn Thread.stop () aufgerufen wird. Gibt es einen Haken, den ich vermisse? download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/guide/…
spurserh
Ich glaube nicht, dass es einen Haken gibt. Vielleicht stellen Sie sich dort einen Haken vor, der nicht da ist. Wenn wir eine explizite Eingabe machen throw, wird der finallyBlock wie erwartet ausgeführt. try { throw new ThreadDeath(); } finally { System.err.println("?"); }
Tom Hawtin - Tackline
9

Das Sun-Tutorial wurde hier in diesem Thread falsch zitiert.

Hinweis: Wenn die JVM - Exits , während der Versuch oder Fang Code ausgeführt wird, dann wird der finally - Block wird nicht ausgeführt. Wenn der Thread, der den Try- oder Catch-Code ausführt, unterbrochen oder beendet wird, wird der finally-Block ebenfalls nicht ausgeführt, obwohl die gesamte Anwendung fortgesetzt wird.

Wenn Sie sich das Sun-Tutorial genau ansehen, um es endgültig zu blockieren, heißt es nicht "wird nicht ausgeführt", sondern "wird möglicherweise nicht ausgeführt". Hier ist die korrekte Beschreibung

Hinweis: Wenn die JVM beendet wird, während der Try- oder Catch-Code ausgeführt wird, wird der finally-Block möglicherweise nicht ausgeführt. Wenn der Thread, der den Try- oder Catch-Code ausführt, unterbrochen oder beendet wird, wird der finally-Block möglicherweise nicht ausgeführt, obwohl die gesamte Anwendung fortgesetzt wird.

Der offensichtliche Grund für dieses Verhalten ist, dass der Aufruf von system.exit () in einem Laufzeit-System-Thread verarbeitet wird, der einige Zeit zum Herunterfahren des JVM benötigen kann, während der Thread-Scheduler die endgültige Ausführung anfordern kann. Schließlich ist es so konzipiert, dass es immer ausgeführt wird. Wenn Sie jedoch jvm herunterfahren, kann es vorkommen, dass jvm heruntergefahren wird, bevor es endgültig ausgeführt wird.

Saurabh
quelle
6

Auch wenn ein Deadlock / Livelock innerhalb des tryBlocks auftritt .

Hier ist der Code, der dies demonstriert:

public class DeadLocker {
    private static class SampleRunnable implements Runnable {
        private String threadId;
        private Object lock1;
        private Object lock2;

        public SampleRunnable(String threadId, Object lock1, Object lock2) {
            super();
            this.threadId = threadId;
            this.lock1 = lock1;
            this.lock2 = lock2;
        }

        @Override
        public void run() {
            try {
                synchronized (lock1) {
                    System.out.println(threadId + " inside lock1");
                    Thread.sleep(1000);
                    synchronized (lock2) {
                        System.out.println(threadId + " inside lock2");
                    }
                }
            } catch (Exception e) {
            } finally {
                System.out.println("finally");
            }
        }

    }

    public static void main(String[] args) throws Exception {
        Object ob1 = new Object();
        Object ob2 = new Object();
        Thread t1 = new Thread(new SampleRunnable("t1", ob1, ob2));
        Thread t2 = new Thread(new SampleRunnable("t2", ob2, ob1));
        t1.start();
        t2.start();
    }
}

Dieser Code erzeugt die folgende Ausgabe:

t1 inside lock1
t2 inside lock1

und "endlich" wird nie gedruckt

wheleph
quelle
3
Technisch gesehen wird der try-Block niemals beendet, sodass der finally-Block niemals eine Chance zur Ausführung erhalten sollte. Gleiches gilt für eine Endlosschleife.
Jeff Mercado
6

Wenn die JVM beendet wird, während der Try- oder Catch-Code ausgeführt wird, wird der finally-Block möglicherweise nicht ausgeführt. ( Quelle )

Normales Herunterfahren - Dies tritt entweder auf, wenn der letzte Nicht-Daemon-Thread beendet wird, oder wenn Runtime.exit () ( Quelle )

Wenn ein Thread beendet wird, führt die JVM eine Bestandsaufnahme der ausgeführten Threads durch. Wenn nur noch Daemon-Threads übrig sind, wird ein ordnungsgemäßes Herunterfahren eingeleitet. Wenn die JVM angehalten wird, werden alle verbleibenden Daemon-Threads abgebrochen und die Blöcke nicht ausgeführt. Die Stapel werden nicht abgewickelt. Die JVM wird gerade beendet. Daemon-Threads sollten sparsam verwendet werden. Nur wenige Verarbeitungsaktivitäten können jederzeit ohne Bereinigung sicher abgebrochen werden. Insbesondere ist es gefährlich, Daemon-Threads für Aufgaben zu verwenden, die möglicherweise E / A-Vorgänge ausführen. Daemon-Threads werden am besten für "Housekeeping" -Aufgaben gespeichert, z. B. für einen Hintergrund-Thread, der abgelaufene Einträge regelmäßig aus einem speicherinternen Cache entfernt. ( Quelle )

Der letzte Nicht-Daemon-Thread beendet das Beispiel:

public class TestDaemon {
    private static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                while (true) {
                    System.out.println("Is alive");
                    Thread.sleep(10);
                    // throw new RuntimeException();
                }
            } catch (Throwable t) {
                t.printStackTrace();
            } finally {
                System.out.println("This will never be executed.");
            }
        }
    };

    public static void main(String[] args) throws InterruptedException {
        Thread daemon = new Thread(runnable);
        daemon.setDaemon(true);
        daemon.start();
        Thread.sleep(100);
        // daemon.stop();
        System.out.println("Last non-daemon thread exits.");
    }
}

Ausgabe:

Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Last non-daemon thread exits.
Is alive
Is alive
Is alive
Is alive
Is alive
Mike
quelle
1

In folgenden Fällen wird der endgültige Block nicht ausgeführt: -

  • Wann System.exit(0)wird vom tryBlock aufgerufen .
  • Wenn JVM nicht mehr genügend Speicher hat
  • Wenn Ihr Java-Prozess von Task-Manager oder Konsole gewaltsam beendet wird
  • Deadlock-Bedingung in Ihrem tryBlock
  • Wenn Ihre Maschine aufgrund eines Stromausfalls herunterfährt

Es kann auch andere Randfälle geben, in denen der endgültige Block nicht ausgeführt wird.

Mangu Singh Rajpurohit
quelle
1

Es gibt zwei Möglichkeiten, die Blockcodeausführung endgültig zu stoppen:
1. Verwenden Sie System.exit ();
2. Wenn die Ausführungskontrolle irgendwie nicht zum Blockieren reicht.
Sehen:

public class Main
{
  public static void main (String[]args)
  {
    if(true){
        System.out.println("will exceute");
    }else{
        try{
            System.out.println("result = "+5/0);
        }catch(ArithmeticException e){
          System.out.println("will not exceute");
        }finally{
          System.out.println("will not exceute");  
        }
    }
  }
}
Aagam Jain
quelle
0

Ich bin auf einen sehr speziellen Fall gestoßen, in dem der finally-Block nicht speziell im Zusammenhang mit dem Spiel-Framework ausgeführt wird.

Ich war überrascht, dass der finally-Block in diesem Controller-Aktionscode erst nach einer Ausnahme aufgerufen wurde, jedoch nie, wenn der Aufruf tatsächlich erfolgreich war.

try {
    InputStream is = getInputStreamMethod();
    renderBinary(is, "out.zip");
catch (Exception e) {
    e.printStackTrace();
} finally {
    cleanUp();
}

Möglicherweise wird der Thread beendet oder etwas, wenn renderBinary () aufgerufen wird. Ich würde vermuten, dass dasselbe bei anderen render () -Aufrufen passiert, aber ich habe es nicht überprüft.

Ich habe das Problem gelöst, indem ich renderBinary () nach dem try / catch nach verschoben habe. Weitere Untersuchungen ergaben, dass play eine @ Final-Annotation bereitstellt, um eine Methode zu erstellen, die ausgeführt wird, nachdem eine Controller-Aktion ausgeführt wurde. Die Einschränkung hierbei ist, dass dies nach der Ausführung einer beliebigen Aktion in der Steuerung aufgerufen wird, sodass dies möglicherweise nicht immer eine gute Wahl ist.

Jeremy Goodell
quelle
-1
//If ArithmeticException Occur Inner finally would not be executed
class Temp
{
    public static void main(String[] s)
    {
        try
        {
        int x = 10/s.length;
        System.out.println(x);
        try
            {
                int z[] = new int[s.length];
                z[10] = 1000;
            }catch(ArrayIndexOutOfBoundsException e)
            {
                System.out.println(e);
            }
         finally
        {
            System.out.println("Inner finally");
        }
        }
        catch(ArithmeticException e)
        {
            System.out.println(e);
        }
    finally 
    {
        System.out.println("Outer Finally"); 
    }

System.out.println("Remaining Code");   
}
}
Shyam Arora
quelle
Verbessern Sie die Einrückung und fügen Sie einige Details hinzu.
ROMANIA_engineer
1
Die Ausführung würde nicht einmal zum inneren Try-Block gelangen, natürlich würde der innere schließlich nicht ausgeführt werden.
Anton Arhipov