Ich habe einen benutzerdefinierten Klassenlader, damit eine Desktop-Anwendung dynamisch Klassen von einem AppServer laden kann, mit dem ich sprechen muss. Wir haben dies getan, da die Menge an Gläsern, die dazu benötigt werden, lächerlich ist (wenn wir sie versenden wollten). Wir haben auch Versionsprobleme, wenn wir die Klassen zur Laufzeit nicht dynamisch aus der AppServer-Bibliothek laden.
Jetzt bin ich auf ein Problem gestoßen, bei dem ich mit zwei verschiedenen AppServern sprechen muss, und habe festgestellt, dass ich je nach den Klassen, die ich zuerst lade, möglicherweise schlecht kaputt gehe ... Gibt es eine Möglichkeit, das Entladen der Klasse zu erzwingen, ohne die JVM tatsächlich zu beenden?
Hoffe das macht Sinn
java
classloader
el_eduardo
quelle
quelle
Antworten:
Die einzige Möglichkeit, eine Klasse zu entladen, besteht darin, dass der verwendete Classloader Müllsammlung ist. Dies bedeutet, dass Verweise auf jede einzelne Klasse und auf den Klassenlader selbst den Weg des Dodos gehen müssen.
Eine mögliche Lösung für Ihr Problem besteht darin, für jede JAR-Datei einen Classloader und für jeden AppServer einen Classloader zu haben, der das tatsächliche Laden von Klassen an bestimmte Jar-Klassenlader delegiert. Auf diese Weise können Sie für jeden App-Server auf verschiedene Versionen der JAR-Datei verweisen.
Dies ist jedoch nicht trivial. Die OSGi-Plattform versucht genau dies zu tun, da jedes Bundle einen anderen Klassenladeprogramm hat und Abhängigkeiten von der Plattform aufgelöst werden. Vielleicht wäre eine gute Lösung, einen Blick darauf zu werfen.
Wenn Sie OSGI nicht verwenden möchten, besteht eine mögliche Implementierung darin, für jede JAR-Datei eine Instanz der JarClassloader- Klasse zu verwenden.
Erstellen Sie eine neue MultiClassloader-Klasse, die Classloader erweitert. Diese Klasse verfügt intern über ein Array (oder eine Liste) von JarClassloadern und durchläuft in der defineClass () -Methode alle internen Klassenloader, bis eine Definition gefunden wird oder eine NoClassDefFoundException ausgelöst wird. Es können verschiedene Zugriffsmethoden bereitgestellt werden, um der Klasse neue JarClassloader hinzuzufügen. Es gibt mehrere mögliche Implementierungen für einen MultiClassLoader im Internet, sodass Sie möglicherweise nicht einmal Ihre eigenen schreiben müssen.
Wenn Sie für jede Verbindung zum Server einen MultiClassloader instanziieren, ist es grundsätzlich möglich, dass jeder Server eine andere Version derselben Klasse verwendet.
Ich habe die MultiClassloader-Idee in einem Projekt verwendet, in dem Klassen, die benutzerdefinierte Skripte enthielten, aus dem Speicher geladen und entladen werden mussten, und es funktionierte recht gut.
quelle
Ja, es gibt Möglichkeiten, Klassen zu laden und später zu "entladen". Der Trick besteht darin, einen eigenen Klassenlader zu implementieren, der sich zwischen dem Klassenlader auf hoher Ebene (dem Systemklassenlader) und den Klassenladern der App-Server befindet, und zu hoffen, dass die Klassenlader des App-Servers das Klassenladen an die oberen Lader delegieren .
Eine Klasse wird durch ihr Paket, ihren Namen und den ursprünglich geladenen Klassenlader definiert. Programmieren Sie einen "Proxy" -Klassenlader, der als erster beim Starten der JVM geladen wird. Arbeitsablauf:
java.x
undsun.x
an den Systemklassenlader (diese dürfen nicht über einen anderen Klassenlader als den Systemklassenlader geladen werden).Genau dort sollte keine ClassCastException oder LinkageError usw. auftreten.
Weitere Informationen zu Klassenladehierarchien (ja, genau das implementieren Sie hier; -) finden Sie unter "Serverbasierte Java-Programmierung" von Ted Neward. Dieses Buch hat mir geholfen, etwas zu implementieren, das dem sehr ähnlich ist, was Sie wollen.
quelle
ClassLoader
ist es etwas zu viel, eine pro Klasse zu haben, eineClassLoader
pro JAR macht Sinn. Könnte genauer sein, wie das Hochladen von Klassen im vorgeschlagenen Schema erzwungen werden soll? Wie kann ich beispielsweise garantieren, dass Instanzen von Klassen, die von ClassLoaderA geladen wurden, nicht von Instanzen referenziert werden, die von ClassLoaderB geladen wurden?Ich habe einen benutzerdefinierten Klassenlader geschrieben, aus dem einzelne Klassen entladen werden können, ohne den Klassenlader zu GCen. Jar Class Loader
quelle
JarClassLoader
für jede geladene JAR-Datei eine haben, können Sie diese aufrufengetLoadedClasses()
, dann über jede iterieren und sie entladen.Klassenlader können ein heikles Problem sein. Sie können insbesondere dann auf Probleme stoßen, wenn Sie mehrere Klassenladeprogramme verwenden und deren Interaktionen nicht klar und genau definiert sind. Ich denke, um tatsächlich eine Klasse entladen zu können, müssen Sie alle Verweise auf Klassen (und deren Instanzen) entfernen, die Sie entladen möchten.
Die meisten Leute, die solche Dinge tun müssen, verwenden OSGi . OSGi ist sehr leistungsfähig und überraschend leicht und einfach zu bedienen.
quelle
Sie können einen ClassLoader entladen, aber Sie können bestimmte Klassen nicht entladen. Insbesondere können Sie keine Klassen entladen, die in einem ClassLoader erstellt wurden, der nicht unter Ihrer Kontrolle steht.
Wenn möglich, schlage ich vor, Ihren eigenen ClassLoader zu verwenden, damit Sie entladen können.
quelle
Klassen haben einen impliziten starken Verweis auf ihre ClassLoader-Instanz und umgekehrt. Sie sind Müll, der wie bei Java-Objekten gesammelt wird. Ohne die Werkzeugoberfläche oder ähnliches zu treffen, können Sie einzelne Klassen nicht entfernen.
Wie immer können Speicherlecks auftreten. Jeder starke Hinweis auf eine Ihrer Klassen oder Klassenlader wird das Ganze auslaufen lassen. Dies tritt beispielsweise bei den Sun-Implementierungen von ThreadLocal, java.sql.DriverManager und java.beans auf.
quelle
Wenn Sie live beobachten, ob das Entladen einer Klasse in JConsole funktioniert hat, fügen Sie
java.lang.System.gc()
am Ende Ihrer Klasse eine Entladelogik hinzu. Es löst explizit Garbage Collector aus.quelle