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
:
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?
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.
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
undputstatic
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:
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.
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.
Tags und Links java jvm linker-errors