Wie die analyse eines NoClassDefFoundError verursacht durch einen ignoriert ExceptionInInitializerError?
Heute verbrachte ich meinen Nachmittag mit der Analyse einer NoClassDefFoundError. Nach überprüfung der classpath wieder und wieder, es stellte sich heraus, dass es ein statisches Mitglied einer Klasse, die warf eine Ausnahme, wurde nicht das erste mal. Nach, dass jede Verwendung der Klasse werfen NoClassDefFoundError ohne einen aussagekräftigen stacktrace:
Exception in thread "main" java.lang.NoClassDefFoundError:
Could not initialize class InitializationProblem$A
at InitializationProblem.main(InitializationProblem.java:19)
Das ist alles. Keine weiteren Zeilen mehr.
Reduziert bis zu dem Punkt, das war das problem:
public class InitializationProblem {
public static class A {
static int foo = 1 / 0;
static String getId() {
return "42";
}
}
public static void main( String[] args ) {
try {
new A();
}
catch( Error e ) {
//ignore the initialization error
}
//here an Error is being thrown again,
//without any hint what is going wrong.
A.getId();
}
}
Damit es nicht ganz so einfach, alle, aber die letzten call of A.getId()
war irgendwo versteckt in der Initialisierungs-code, der ein sehr großes Projekt.
Frage:
Jetzt, dass ich gefunden habe, dieser Fehler nach Stunden von Versuch und Irrtum, ich Frage mich, ob es ein straight forward Weg zu finden, diese Fehler ab der ausgelösten Ausnahme. Irgendwelche Ideen auf, wie dies zu tun?
Ich hoffe, diese Frage wird ein Hinweis für alle anderen die Auswertung einer unerklärlichen NoClassDefFoundError
.
- Meine erste Idee wäre nicht zu fangen und zu ignorieren
Errors
. Je. - Dies war eine Initialisierung einige test-Klassen gesammelt aus dem Dateisystem. Ich versuchte jede Klasse instanziieren, um zu sehen, ob Sie mit einer gegebenen Oberfläche... Manche Klassen können nicht instanziiert werden, wie z.B. die abstrakten. Also habe ich alle Fehler abgefangen und ignoriert Sie.
- Zumindest, drucken Sie die stack-trace. Ein leerer catch-block sollte eine rote Flagge, um Sie. (Haben Sie jemals verwendet FindBugs? Es wird helfen, fangen viele Dinge viel früher, als würden Sie auf Ihre eigenen.)
- Sie brauchen nicht zu instanziieren einer Klasse, um zu sehen, ob es eine Schnittstelle implementiert. Um zu sehen, ob ein
Class<?> someUnknownClass
implementiert einige interface IInterface tunIInterface.class.isAssignableFrom(someUnknownClass)
- Eigentlich hatte ich eine Klasse.forName(), und auch dies den Fehler verursacht. Jetzt bin ich auf der Suche nach einem besseren Weg, siehe stackoverflow.com/questions/2210930/...
- die Antwort ist in Ihrem eigenen Kommentar. Ignorieren Sie nicht die Ausnahmen überprüfen, ob die Klasse Abstrakt oder nicht
Du musst angemeldet sein, um einen Kommentar abzugeben.
Mein Rat wäre, um dieses problem zu vermeiden, durch die Vermeidung von statischen Initialisierungen, so viel wie Sie können. Da diese Initialisierungen, die ausgeführt werden, während der Prozess zum laden von Klassen, viele Rahmenbedingungen nicht behandeln Sie sehr gut, und in der Tat ältere VMs nicht behandeln Sie sehr gut, entweder.
Meisten (wenn nicht alle) statische Initialisierungen umgestaltet werden kann in andere Formen, und im Allgemeinen macht es die Probleme einfacher zu behandeln und zu diagnostizieren. Wie hast du Sie entdeckt, statische Initialisierungen sind verboten zu werfen checked exceptions, also muss man sich entweder anmelden-und-ignorieren, oder melden-und-rethrow-als-deaktiviert, von denen keines die Aufgabe des Diagnose einfacher.
Auch die meisten classloadern, machen ein-und-nur-ein Versuch zum laden einer bestimmten Klasse, und wenn es nicht das erste mal, und ist nicht richtig behandelt, das problem wird effektiv gequetscht, und Sie am Ende mit generischer Fehler geworfen wird, mit wenig oder keinem Zusammenhang.
Wirklich, Sie sollten nie jemals fangen Fehler, aber hier ist, wie können Sie finden Initialisierer Probleme, wo diese auftreten.
Hier ist ein agent, der alle ExceptionInInitializerErrors drucken Sie die stack-trace, wenn Sie erstellt werden:
Es verwendet javassist zu ändern, die Klassen. Kompilieren und steckte es in eine jar-Datei mit javassist Klassen und das folgende MANIFEST.MF
Führen Sie Ihre app mit
java -javaagent:agentjar.jar MainClass
und jeder ExceptionInInitializerError gedruckt werden, auch wenn es abgefangen wird.NoClassDefFoundError
nach, dass, wenn ich mich nicht Irre. Können wir immer noch Referenz auf das original -ExceptionInInitializerError
in nachfolgenden aufrufen?Can-Retransform-Classes: true
auf das manifest -, sonst werden Sie etwas sehen wiejava.lang.UnsupportedOperationException: adding retransformable transformers is not supported in this environment
. Offenbar gilt auch für die IBM JVM, die, wie ich die Lösung gefunden, über die Google-Suche.Wenn Sie jemals sehen-code mit diesem Muster:
Herauszufinden, wer schrieb es und SCHLAGEN DIE SCHEIßE aus IHNEN HERAUS. Ich bin ernst. Versuchen Sie, Sie zu erhalten abgefeuert-Sie verstehen nicht, das Debuggen Teil der Programmierung in irgendeiner Weise, Form oder form.
Ich denke, wenn Sie einen Lehrling Programmierer können Sie nur schlagen die Scheiße aus Ihnen, und dann lassen Sie Sie haben EINE zweite chance.
Sogar für den temporären code--es ist nie Wert, die Möglichkeit, dass es irgendwie nach vorne gebracht werden, in die Produktion code.
Diese Art von code, der verursacht wird von geprüften Ausnahmen, die eine ansonsten vernünftige Idee in eine riesige Sprache Fallstrick durch die Tatsache, dass an einem gewissen Punkt werden wir alle sehen code wie oben.
Kann es TAGE, wenn nicht WOCHEN, um dieses problem zu lösen. Also du musst verstehen, dass durch die Kodierung, die Sie möglicherweise kostet das Unternehmen Zehntausende von Dollar. (Gibt es eine andere gute Lösung, eine Strafe für alle das Gehalt ausgegeben, weil der, der Dummheit-ich Wette, Sie nie wieder tun).
Wenn Sie erwarten, dass Sie (catch) ein Fehler und es behandeln, stellen Sie sicher, dass:
Wenn ich sound aggressiv und wütend, es ist, weil ich habe mich daran geschraubt verbringen Wochen finden von versteckten bugs wie dieses, und, als Berater, habe niemanden gefunden, es zu nehmen auf. Sorry.
ExceptionInInitializerError
, die erste Zeit, aber nach, dass ich einNoClassDefFoundError
. Gibt es eine Möglichkeit, die Verbindung zu den ursprünglichenExceptionInInitializerError
?Nur die Hinweise, die Fehler, die Sie gibt den Namen der Klasse und dass etwas ging schrecklich falsch bei der Initialisierung der Klasse. Also entweder in eines dieser statischen Initialisierungen, Feld-Initialisierung oder vielleicht in einen sogenannten Konstruktor.
Der zweite Fehler wurde ausgelöst, weil die Klasse noch nicht initialisiert wurde, zu der Zeit A. getId() aufgerufen wurde. Die erste Initialisierung abgebrochen wurde. Fang, der Fehler war ein schöner test für das engineering-team 😉
Einen viel versprechenden Ansatz zu finden ein Fehler ist, zum initialisieren der Klasse in einer Testumgebung testen und Debuggen Sie die Initialisierung (einzelne Schritte) - code. Dann sollte man in der Lage sein zu finden, die Ursache des Problems.
Es ist Ihr problem! Nicht immer fangen und Fehler ignorieren (oder Throwable). NICHT IMMER.
Und wenn Sie geerbt haben einige riskante code, der könnte dies tun, verwenden Sie Ihre Lieblings-code-search-tool /IDE zu suchen und zerstören die problematische
catch
Klauseln.Nein gibt es nicht. Es gibt komplizierte/heroische Weise ... wie mit tun kluge Dinge mit einem java-agent-hack-runtime-system auf dem Fliegen ... aber nicht die Art von Sache, die eine typische Java-Entwickler wahrscheinlich zu haben in Ihrem "toolbox".
Welches ist, warum die Beratung über das so wichtig ist.
Ich verstehe wirklich nicht, Ihre Argumentation. Sie Fragen "finden Sie diese Fehler ab der ausgelösten Ausnahme", und noch Sie fangen, dass Fehler und ignorieren es ...
Wenn Sie das problem reproduzieren können (auch gelegentlich), und es ist möglich, dass die app unter debug, dann können Sie legen Sie einen Haltepunkt in Ihrem debugger für (alle 3 Konstruktoren) ExceptionInInitializerError, und sehen, wenn Sie git Treffer.