Seltsamer Ausnahmetabelleneintrag, der von Javac von Sun erzeugt wurde

Lesezeit: 6 Minuten

Benutzer-Avatar
aiobe

Angesichts dieses Programms:

class Test {
    public static void main(String[] args) {
        try {
            throw new NullPointerException();
        } catch (NullPointerException npe) {
            System.out.println("In catch");
        } finally {
            System.out.println("In finally");
        }
    }
}

Sonne javac (v 1.6.0_24) erzeugt den folgenden Bytecode:

public static void main(java.lang.String[]);

        // Instantiate / throw NPE
   0:   new     #2;         // class NullPointerException
   3:   dup
   4:   invokespecial   #3; // Method NullPointerException."<init>":()V
   7:   athrow

        // Start of catch clause
   8:   astore_1
   9:   getstatic       #4; // Field System.out
   12:  ldc     #5;         // "In catch"
   14:  invokevirtual   #6; // Method PrintStream.println
   17:  getstatic       #4; // Field System.out

        // Inlined finally block
   20:  ldc     #7;         // String In finally
   22:  invokevirtual   #6; // Method PrintStream.println
   25:  goto    39

        // Finally block
        // store "incomming" exception(?)
   28:  astore_2
   29:  getstatic       #4; // Field System.out
   32:  ldc     #7;         // "In finally"
   34:  invokevirtual   #6; // Method PrintStream.println

        // rethrow "incomming" exception
   37:  aload_2
   38:  athrow

   39:  return

Mit folgender Ausnahmetabelle:

  Exception table:
   from   to  target type
     0     8     8   Class NullPointerException
     0    17    28   any
    28    29    28   any

Meine Frage ist: Warum um alles in der Welt enthält es diesen letzten Eintrag in der Ausnahmetabelle?!

So wie ich es verstehe, heißt es im Grunde “wenn die astore_2 eine Ausnahme auslöst, abfangen und dieselbe Anweisung wiederholen“.

Ein solcher Eintrag wird auch mit leeren Try / Catch / Final-Klauseln wie z

try {} catch (NullPointerException npe) {} finally {}

Einige Beobachtungen

  • Der Eclipse-Compiler erzeugt keinen solchen Ausnahmetabelleneintrag
  • Die JVM-Spezifikation dokumentiert keine Laufzeitausnahmen für das astore Anweisung.
  • Ich weiß, dass es legal ist, die JVM zu werfen VirtualMachineError für jede Anweisung. Ich denke, der eigenartige Eintrag verhindert, dass sich solche Fehler aus dieser Anweisung ausbreiten.

  • Ich werde dies als Kommentar posten, da ich es nicht geschafft habe, mich um dieses Konzept zu kümmern. Zu diesem Thema gibt es einen Eintrag, warum der letzte Eintrag bei generiert wird ein Blog. Anscheinend ist das Verhalten des Compilers zum Kompilieren des finally-Blocks, wie in der VM-Spezifikation angegeben, etwas daneben, was den Sun/Oracle-Compiler betrifft. Der letzte Ausnahmetabelleneintrag ist vorhanden, um den “erzeugten Ausnahmehandler” zu schützen. Ich habe nicht herausgefunden, wie die Wache funktioniert und warum sie so funktionieren sollte.

    – Vineet Reynolds

    17. Juni 2011 um 15:43 Uhr

Dafür gibt es nur zwei mögliche Erklärungen: Der Compiler enthält einen Fehler oder er setzt aus obskuren Gründen eine Art Wasserzeichen.

Dieser Eintrag ist sicherlich falsch, da jede Ausnahme, die von einem finally-Block selbst ausgelöst wird, den Ausführungsfluss an den äußeren Ausnahmehandler oder den finally-Block senden muss, aber niemals den gleichen finally-Block “erneut ausführen” muss.

Ein guter Beweis dafür, dass es sich um einen Fehler/ein Wasserzeichen handelt, ist auch die Tatsache, dass Eclipse (und vielleicht andere Java-Compiler) keinen solchen Eintrag generieren, und trotzdem funktionieren von Eclipse generierte Klassen auf Suns JVM einwandfrei.

Dieser Beitrag ist jedoch interessant, da die Klassendatei anscheinend gültig und verifiziert ist. Wenn ich ein JVM-Implementierer wäre, würde ich diesen Eintrag ignorieren und einen Fehler für Sun/Oracle füllen!

  • Jede Ausnahme, die von einem finally-Block selbst ausgelöst wird, muss den Ausführungsfluss an den äußeren Ausnahmehandler senden” — Nein, nicht, wenn Sie einen inneren Catch-Block haben. Außerdem verwechseln Sie Java mit Bytecode: Das Bytecode-Programm sollte die Semantik des zu kompilierenden Java-Programms implementieren. Es kann dies tun, indem es alle wirft / fängt Arten von verrückten Ausnahmen. Es gibt keinen “richtigen” oder “falschen” Weg, das Programm zu kompilieren. Mein Verdacht ist, dass der Eintrag ein Nebeneffekt eines allgemeineren Konstrukts im Compiler ist. Vielleicht wird ein solcher Eintrag sogar vom ausgegeben Eclipse-Compiler in einigen seltenen Ausnahmefällen.

    – aiobe

    20. Juni 2011 um 18:45 Uhr

  • @aioobe Während JVM anstelle von Java ein Ziel für andere Sprachen sein kann, zeigt diese Frage explizit, dass ein Java-Programm für JVM kompiliert wird. Die JVM kann zulassen, dass eine Ausnahme von genau dem Code behandelt wird, der sie ausgelöst hat, aber eine store_2-Anweisung gehört zu einem Java-finally-Block, der keinen inneren Versuch hat, sodass jede von ihr ausgelöste Ausnahme den Ausführungsfluss tatsächlich an einen äußeren Ausnahmehandler senden muss. “Mein Verdacht ist, dass der Eintrag ein Nebeneffekt eines allgemeineren Konstrukts im Compiler ist.” – Das denke ich auch, aber trotzdem bricht dies den Java-Sprachvertrag.

    – Fernakolo

    20. Juni 2011 um 19:36 Uhr

  • dies bricht den Java-Sprachvertrag – Nein, tut es nicht. Sie könnten zum Beispiel so argumentieren: Jede Ausnahme, die von einer Anweisung innerhalb der finally-Klausel ausgelöst wird, sollte aus der Klausel heraus propagiert werden. Wenn also keine Anweisungen in der finally-Klausel vorhanden sind, können von dort keine Ausnahmen propagiert werden. Daher ist es völlig legal, alle Ausnahmen im “kompilierten finally-Block” zu unterdrücken, wenn der kompilierte Block keine Anweisungen enthielt.

    – aiobe

    20. Juni 2011 um 19:47 Uhr

  • @aioobe Ja, aber der betreffende finally-Block enthält Anweisungen !!! Eine andere Möglichkeit, zu erklären, warum es sich um einen Fehler handelt: Wenn eine JVM einen VirtualMachineError (oder eine andere Fehlerausnahme) auf die astore_2-Funktion auslöst, kann sie in eine Endlosschleife eintreten, die 100 % einer CPU verbraucht, selbst ohne Schleife im Java-Code, wenn kompiliert mit Javac. Wenn jetzt dieselbe Klasse mit Eclipse kompiliert wurde, sendet der VirtualMachineError den Ausführungsfluss korrekt nach außen.

    – Fernakolo

    20. Juni 2011 um 19:48 Uhr


  • Der betreffende finally-Block enthält keine Anweisungen” — Richtig. Das ist genau der Grund, warum es eigentlich in Ordnung ist, alle Ausnahmen zu unterdrücken, die vom Bytecode ausgelöst werden, der dem No-Statement-Block entspricht. Bezüglich des VirtualMachineError: Dann ist diese Ausnahme auf einen Fehler in der JVM, dh der JVM, zurückzuführen hat den Vertrag gebrochen, bevor die Ausnahme überhaupt erfasst wurde.

    – aiobe

    20. Juni 2011 um 19:50 Uhr


Benutzer-Avatar
Dan Cruz

Wenn ich mir den Quellcode von OpenJDK 7 ansehe, würde ich es wagen, den Grund dafür zu erraten 28 29 28 any Ausnahmetabelleneintrag liegt daran, dass der Code, der die verarbeitet astore Bytecode (vgl Code ab Zeile 1871) kann ein werfen java.lang.LinkageError Ausnahme, wenn der entnommene Wert aus dem Operandenstapel nicht a ist returnAddress oder reference type (siehe die Java Virtual Machine Specification für ein Laden) und sie möchten, dass diese Fehlerbedingung im Stack-Trace angezeigt wird.

Für den Fall, dass der Operandenstapel einen fehlerhaften Operandentyp enthält, löscht die JVM den Operandenstapel (um diesen fehlerhaften Operanden loszuwerden), fügen Sie a LinkageError
auf dem Operandenstapel und führen Sie die aus astore Bytecode erneut, diesmal erfolgreich ausgeführt astore Bytecode mit einer bereitgestellten JVM LinkageError
Objektbezug. Siehe die werfen Dokumentation für weitere Informationen.

Ich würde stark die Ursache für das Werfen a vermuten LinkageError während
astore Die Verarbeitung erfolgt aufgrund der Komplexitäten JSR/RET-Subroutinen Einführung in die Bytecode-Überprüfung (OpenJDK-Änderungen
6878713, 6932496 und 7020373 sind neuere Beweise dafür JSRdie anhaltende Komplexität von ; Ich bin sicher, dass Sun/Oracle andere Closed-Source-Tests hat, die wir in OpenJDK nicht sehen). Das OpenJDK 7020373 Nutzungen ändern LinkageError um Testergebnisse zu validieren/ungültig zu machen.

  • Hmm .. würde ein solcher Typfehler nicht im Bytecode-Überprüfungsschritt abgefangen werden? (Vor dem Ausführen des Codes.)

    – aiobe

    29. September 2011 um 19:23 Uhr

  • Meine Antwort wurde mit Informationen zur Komplexität der JSR/RET-Unterroutine aktualisiert, was die Bytecode-Überprüfung schmerzhaft macht.

    – Dan Cruz

    30. September 2011 um 13:10 Uhr

Mein Verständnis ist, dass der zweite Eintrag in der Ausnahmetabelle die implizite Catch-Everything-Klausel ist, die vom Compiler hinzugefügt wurde, um alle Ausnahmen/Fehler abzudecken, die im Hauptteil oder in den Catch-Handlern ausgelöst werden, und der dritte Eintrag der Wächter für diesen impliziten Catch ist, um den Fluss zu erzwingen durch die endlich Hinrichtung.

1026540cookie-checkSeltsamer Ausnahmetabelleneintrag, der von Javac von Sun erzeugt wurde

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy