Heute habe ich meinen Nachmittag damit verbracht, einen NoClassDefFoundError zu analysieren. Nach dem wiederholten Überprüfen des Klassenpfads stellte sich heraus, dass ein statisches Member einer Klasse eine Exception geworfen hatte, die beim ersten Mal ignoriert wurde. Danach wirft jede Verwendung der Klasse einen NoClassDefFoundError ohne einen aussagekräftigen Stacktrace:
%Vor%Das ist alles. Keine weiteren Zeilen.
Um es nicht so einfach zu machen, wurden alle außer dem letzten Aufruf von A.getId()
irgendwo im Initialisierungscode eines sehr großen Projekts versteckt.
Nun, da ich diesen Fehler nach stundenlangem Ausprobieren gefunden habe, frage ich mich, ob es einen einfachen Weg gibt, um diesen Fehler zu finden, beginnend mit der geworfenen Ausnahme. Irgendwelche Ideen dazu?
Ich hoffe, dass diese Frage ein Hinweis für jeden ist, der eine unerklärliche NoClassDefFoundError
analysiert.
Mein Rat wäre, dieses Problem zu vermeiden, indem Sie statische Initialisierer so gut wie möglich vermeiden. Da diese Initialisierer während des Classloading-Prozesses ausgeführt werden, werden sie von vielen Frameworks nicht sehr gut verarbeitet, und ältere VMs verarbeiten sie auch nicht sehr gut.
Die meisten (wenn nicht alle) statischen Initialisierer können in andere Formen umgestaltet werden, und im Allgemeinen erleichtert dies die Handhabung und Diagnose der Probleme. Wie Sie festgestellt haben, dürfen statische Initialisierer keine geprüften Ausnahmen auslösen. Sie müssen also entweder loggen und ignorieren oder loggen und erneut starten - wenn sie nicht aktiviert sind, wodurch die Diagnose nicht einfacher wird.
Außerdem versuchen die meisten Klassenlader, eine bestimmte Klasse nur einmal zu laden. Wenn sie das erste Mal fehlschlägt und nicht richtig gehandhabt wird, wird das Problem effektiv gequetscht, und Sie erhalten generische Fehler geworfen werden, mit wenig oder keinem Kontext.
Sie sollten eigentlich niemals Fehler finden, aber hier finden Sie Initialisierungsfehler, wo immer sie auftreten könnten.
Hier ist ein Agent, der alle ExceptionInInitializerErrors veranlasst, den Stack-Trace beim Erstellen zu drucken:
%Vor%Es verwendet javassist, um die Klassen zu ändern. Kompiliere und lege es in eine jar-Datei mit Javassist-Klassen und der folgenden MANIFEST.MF
%Vor% Führen Sie Ihre App mit java -javaagent:agentjar.jar MainClass
aus und jedes ExceptionInInitializerError wird gedruckt, selbst wenn es abgefangen wird.
Wenn Sie Code mit diesem Muster jemals sehen:
%Vor%Finden Sie heraus, wer es geschrieben hat und schlagen Sie den Mist aus ihnen heraus. Es ist mein ernst. Versuchen Sie, sie zu feuern - sie verstehen den Debugging-Teil der Programmierung in keiner Weise, Form oder Form.
Ich schätze, wenn sie ein Lehrlingsprogrammer sind, könnten Sie einfach die Scheiße aus ihnen herausschlagen und dann eine zweite Chance haben.
Selbst für temporären Code - es ist niemals die Möglichkeit wert, dass es irgendwie in den Produktionscode gebracht wird.
Diese Art von Code wird durch geprüfte Ausnahmen verursacht, eine ansonsten vernünftige Idee, die durch die Tatsache, dass wir irgendwann einen Code wie oben sehen werden, zu einem großen Sprachfehler wird.
Es kann Tage dauern, wenn nicht Wochen, um dieses Problem zu lösen. Sie müssen also verstehen, dass Sie das Unternehmen potenziell Zehntausende von Dollars kosten. (Es gibt eine andere gute Lösung, sie für alle Gehälter, die wegen dieser Dummheit ausgegeben werden, zu bestrafen - ich wette, dass sie DAS nie wieder tun).
Wenn Sie einen bestimmten Fehler erwarten und behandeln, stellen Sie sicher, dass:
Wenn ich aggressiv und wütend töne, liegt es daran, dass ich Wochen damit verbracht habe, versteckte Fehler zu finden, und als Berater niemanden gefunden habe, der es anstellen könnte. Entschuldigung.
Die einzigen Hinweise, die der Fehler gibt, sind der Name der Klasse und dass etwas während der Initialisierung dieser Klasse schrecklich falsch gelaufen ist. Also entweder in einem dieser statischen Initialisierer, Feldinitialisierung oder vielleicht in einem aufgerufenen Konstruktor.
Der zweite Fehler wurde ausgelöst, weil die Klasse zum Zeitpunkt des Aufrufs von A.getId () nicht initialisiert wurde. Die erste Initialisierung wurde abgebrochen. Diesen Fehler zu finden war ein schöner Test für das Entwicklerteam ;-)
Ein vielversprechender Ansatz zur Lokalisierung eines solchen Fehlers besteht darin, die Klasse in einer Testumgebung zu initialisieren und den Initialisierungscode (Einzelschritte) zu debuggen. Dann sollte man in der Lage sein, die Ursache des Problems zu finden.
Heute habe ich meinen Nachmittag damit verbracht, einen NoClassDefFoundError zu analysieren. Nach dem wiederholten Überprüfen des Klassenpfads, stellte sich heraus, dass es ein statisches Mitglied einer Klasse gab, das eine Exception geworfen hat, die das erste Mal ignoriert wurde .
Da ist dein Problem! Fangen und ignorieren Sie niemals Fehler (oder Throwable). NIEMALS.
Und wenn Sie etwas zwielichtigen Code geerbt haben, der dies tun könnte, verwenden Sie Ihr Lieblings-Code-Such-Tool / IDE, um die problematischen catch
-Klauseln zu suchen und zu zerstören.
Nachdem ich diesen Fehler nach stundenlangem Ausprobieren gefunden habe, frage ich mich, ob es einen einfachen Weg gibt, um diesen Fehler zu finden, beginnend mit der geworfenen Ausnahme.
Nein, ist es nicht. Es gibt komplizierte / heroische Wege ... wie zB clevere Dinge mit einem Java-Agenten zu tun, um das Laufzeitsystem im laufenden Betrieb zu hacken ... aber nicht die Art von Dingen, die ein typischer Java-Entwickler wahrscheinlich in seiner "Toolbox" hat.
Deshalb ist der obige Ratschlag so wichtig.
Wenn Sie das Problem (gelegentlich) reproduzieren können und die Anwendung unter debug ausgeführt werden kann, können Sie möglicherweise einen Unterbrechungspunkt in Ihrem Debugger für (alle 3 Konstruktoren von) ExceptionInInitializerError festlegen und sehen, wann sie auftreten Git Hit.
Tags und Links java debugging noclassdeffounderror