Klassen in Java entladen?

174

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

el_eduardo
quelle
Haben Sie einen Klassenlader für jedes Glas? Wie archivieren OSGI-Container, um Classloader zu entladen? Es sieht so aus, als gäbe es keine Entlade-API in der Classloader-Klasse?
Hetaoblog

Antworten:

190

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.

Mario Ortegón
quelle
31
Beachten Sie auch, dass das Entladen von Klassen laut java.sun.com/docs/books/jls/second_edition/html/… eine Optimierung darstellt und je nach JVM-Implementierung tatsächlich auftreten kann oder nicht.
5
Versuchen Sie als einfachere und leichtere Alternative zu OSGi JBoss Modules - modularisiertes Laden von Klassen mit Classloader pro Modul (eine Gruppe von Jars).
Ondra Žižka
42

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:

  • Das Programm startet und die echte "Haupt" -Klasse wird von diesem Proxy-Klassenladeprogramm geladen.
  • Jede Klasse, die dann normalerweise geladen wird (dh nicht durch eine andere Classloader-Implementierung, die die Hierarchie durchbrechen könnte), wird an diesen Klassenlader delegiert.
  • Der Proxy-Klassenlader delegiert java.xund sun.xan den Systemklassenlader (diese dürfen nicht über einen anderen Klassenlader als den Systemklassenlader geladen werden).
  • Instanziieren Sie für jede Klasse, die austauschbar ist, einen Klassenlader (der die Klasse wirklich lädt und nicht an den übergeordneten Klassenlader delegiert) und laden Sie ihn über diesen.
  • Speichern Sie das Paket / den Namen der Klassen als Schlüssel und den Klassenladeprogramm als Werte in einer Datenstruktur (dh Hashmap).
  • Jedes Mal, wenn der Proxy-Klassenladeprogramm eine Anforderung für eine zuvor geladene Klasse erhält, gibt er die Klasse von dem zuvor gespeicherten Klassenladeprogramm zurück.
  • Es sollte ausreichen, das Byte-Array einer Klasse von Ihrem Klassenladeprogramm zu suchen (oder das Schlüssel / Wert-Paar aus Ihrer Datenstruktur zu "löschen") und die Klasse neu zu laden, falls Sie es ändern möchten.

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.

Georgi
quelle
3
Ich verstehe keine Leute, die für diese Antwort -1 angekreuzt haben, ohne einen Kommentar zu hinterlassen. Für mich sieht gut aus. Vielleicht ClassLoaderist es etwas zu viel, eine pro Klasse zu haben, eine ClassLoaderpro 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?
dma_k
@dma_k genau, die Antwort ist gut, aber sie berührt nicht die wichtigsten Punkte, die Sie erwähnt haben.
Zinking
@Georgi Gibt es eine vorhandene Implementierung, die wir dafür initiieren / wiederverwenden können?
Schlitten
1
Es wäre sehr, sehr hilfreich, wenn Sie den Java-Beispielcode bereitstellen könnten. Um genau zu sein, suche ich nach Möglichkeiten zum Entladen von Klassen mit CustomClassLoader, hatte aber kein Glück.
Sriharsha grv
@ Sriharshag.rv Haben Sie dies versucht und ein Beispiel implementiert?
Niaomingjian
17

Ich habe einen benutzerdefinierten Klassenlader geschrieben, aus dem einzelne Klassen entladen werden können, ohne den Klassenlader zu GCen. Jar Class Loader

Kamran
quelle
Klappt wunderbar :). Gibt es eine Methode zum Entladen aller Klassendateien einer JAR-Datei?
Ercksen
Leider momentan nicht. Aber ich werde mich darum kümmern. Kann in zukünftigen Versionen sein.
Kamran
Übrigens habe ich eine kleine Problemumgehung gefunden. Wenn Sie JarClassLoaderfür jede geladene JAR-Datei eine haben, können Sie diese aufrufen getLoadedClasses(), dann über jede iterieren und sie entladen.
Ercksen
12

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.

Steve g
quelle
7

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.

Jason Cohen
quelle
4

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.

Tom Hawtin - Tackline
quelle
-1

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.

Aleksander Drozd
quelle
3
Achtung: System.gc () muss GC nicht aufrufen. Es fordert das JVM nur auf, es zu starten, erzwingt es jedoch nicht. Und IME startet es oft nicht den GC: - \
Juh_