Wird von der JVM geworfen, wenn eine nicht verwendete Klasse fehlt?

8

Betrachten Sie das Programm:

%Vor%

Ist Foo im Klassenpfad der Laufzeit erforderlich, wenn das Programm ohne Argumente gestartet wird?

Forschung

Die Java-Sprachspezifikation ist ziemlich vage, wenn Verknüpfungsfehler gemeldet werden:

  

Diese Spezifikation ermöglicht eine flexible Implementierung, wenn Aktivitäten (und, wegen Rekursion, Laden) miteinander verknüpft werden, vorausgesetzt, dass die Semantik der Programmiersprache Java respektiert wird, dass eine Klasse oder Schnittstelle vollständig verifiziert und vorbereitet wird wird initialisiert, und Fehler, die während der Verknüpfung erkannt werden, werden an einem Punkt im Programm ausgelöst, an dem vom Programm eine Aktion ausgeführt wird, die möglicherweise eine Verknüpfung mit der am Fehler beteiligten Klasse oder Schnittstelle erfordert.

Meine Tests zeigen an, dass LinkageErrors nur ausgelöst werden, wenn ich tatsächlich Foo :

verwende %Vor%

Kann man sich auf dieses Verhalten verlassen? Oder gibt es eine Mainstream-JVM, die ungenutzten Code verknüpft? Wenn ja, wie kann ich nicht verwendeten Code isolieren, sodass er nur bei Bedarf verknüpft wird?

    
meriton 31.01.2017, 19:01
quelle

3 Antworten

6

Sie brauchen nur kleine Änderungen an Ihrem Testcode, um diese Frage zu beantworten.

Ändern Sie die Typhierarchie in

%Vor%

und das Programm zu

%Vor%

Nun wird das Programm mit einem Fehler fehlschlagen, wenn Foo nicht vorhanden ist, noch bevor die Methode main (mit HotSpot) eingegeben wurde. Der Grund dafür ist, dass der Verifizierer die Definition von Foo benötigt, um zu überprüfen, ob die Übergabe an eine Methode, die Bar erwartet, gültig ist.

HotSpot verwendet eine Abkürzung und lädt den Typ nicht, wenn die Typen genau übereinstimmen oder wenn der Zieltyp java.lang.Object ist, wobei die Zuweisung immer gültig ist. Deshalb wird der ursprüngliche Code nicht früh ausgegeben, wenn Foo nicht vorhanden ist.

Die Quintessenz ist, dass der genaue Zeitpunkt, zu dem ein Fehler ausgelöst wird, implementierungsabhängig ist, z. könnte von der tatsächlichen Verifier-Implementierung abhängen. Alles, was garantiert ist, ist, wie Sie bereits erwähnten, dass ein Versuch, eine Aktion durchzuführen, die eine Verknüpfung erfordert, zuvor erkannte Verknüpfungsfehler auslösen wird. Aber es ist durchaus möglich, dass Ihr Programm nie so weit kommt, um einen Versuch zu machen.

    
Holger 31.01.2017, 23:42
quelle
4

Ich denke, so etwas ist undefiniert (irgendwie, siehe unten). Wir wissen, wie es für die Orakel-VM funktioniert, aber es ist ein Implementierungsdetail der VM. Eine VM könnte auch alle Klassen sofort laden.

Was Sie in der VM-Spezifikation (Hervorhebung von mir):

  

Das Verknüpfen einer Klasse oder Schnittstelle beinhaltet das Überprüfen und Vorbereiten dieser Klasse oder Schnittstelle, ihrer direkten Superklasse, ihrer direkten Superschnittstellen und ihres Elementtyps (falls es ein Array-Typ ist), falls erforderlich. Die Auflösung symbolischer Referenzen in der Klasse oder der Schnittstelle ist ein optionaler Teil der Verknüpfung.

     

Diese Spezifikation ermöglicht eine flexible Implementierung, wenn es darum geht, Aktivitäten zu verknüpfen (und aufgrund von Rekursion, Laden) ...

Und weiter unten:

  

Die Befehle der Java Virtual Machine anewarray , checkcast , getfield , getstatic , instanceof , invokedynamic , invokeinterface , invokespecial , invokestatic , invokevirtual , ldc , ldc_w , multianewarray , new , putfield und putstatic stellen symbolische Verweise auf den Laufzeitkonstanzpool her. Die Ausführung einer dieser Anweisungen erfordert eine Auflösung der symbolischen Referenz.

     

Auflösung ist der Prozess der dynamischen Bestimmung konkreter Werte aus symbolischen Referenzen im Laufzeitkonstanzpool.

Die Zeile use(new Foo()); kompiliert zu:

%Vor%

Das würde also die Auflösung von Foo erfordern, aber sonst nichts im Programm.

Es heißt aber auch (an ein Beispiel angehängt, weshalb ich es zuerst vermisste):

  

Welcher Strategie auch immer gefolgt wird, jeder während der Auflösung entdeckte Fehler muss an einen Punkt im Programm geworfen werden, der (direkt oder indirekt) einen symbolischen Verweis auf die Klasse oder Schnittstelle verwendet.

Wenn also bei der Auflösung der Klasse Test ein Fehler gefunden wird, wird der Fehler nur ausgelöst, wenn die fehlerhafte symbolische Referenz tatsächlich verwendet wird.

    
Jorn Vernee 31.01.2017 19:25
quelle
2

Ich muss sagen, dass ich unter Ihren Umständen sehr versucht wäre, Reflektion zu verwenden, um eine Schnittstelle zu schaffen, die immer vorhanden ist, um das Problem vollständig zu umgehen. Etwas in der Art von:

%Vor%

[EDIT] Ich weiß, dass dies nicht direkt die Frage beantwortet, aber das scheint eine bessere Lösung zu sein als wo du reist.

    
Chris Parker 31.01.2017 20:17
quelle

Tags und Links