Décharger des classes en java?

174

J'ai un chargeur de classe personnalisé pour qu'une application de bureau puisse démarrer dynamiquement le chargement de classes à partir d'un serveur AppServer avec lequel je dois parler. Nous l'avons fait car la quantité de bocaux nécessaires pour ce faire est ridicule (si nous voulions les expédier). Nous avons également des problèmes de version si nous ne chargeons pas les classes dynamiquement au moment de l'exécution à partir de la bibliothèque AppServer.

Maintenant, je viens de rencontrer un problème où j'ai besoin de parler à deux AppServers différents et j'ai trouvé que selon les classes que je charge en premier, je pourrais mal casser ... Y a-t-il un moyen de forcer le déchargement de la classe sans réellement tuer la JVM?

J'espère que cela a du sens

el_eduardo
la source
Avez-vous un classloader pour chaque bocal? Comment les conteneurs OSGI archivent-ils pour décharger le chargeur de classe? Il semble qu'il n'y a pas d'API de déchargement dans la classe classloader?
hetaoblog

Réponses:

190

La seule façon dont une classe peut être déchargée est si le chargeur de classe utilisé est garbage collection. Cela signifie que les références à chaque classe et au chargeur de classe lui-même doivent suivre la voie du dodo.

Une solution possible à votre problème consiste à avoir un Classloader pour chaque fichier jar et un Classloader pour chacun des AppServers qui délègue le chargement réel des classes à des classloaders Jar spécifiques. De cette façon, vous pouvez pointer vers différentes versions du fichier jar pour chaque serveur d'applications.

Ce n'est pas anodin, cependant. La plate-forme OSGi s'efforce de faire exactement cela, car chaque bundle a un chargeur de classe différent et les dépendances sont résolues par la plate-forme. Peut-être qu'une bonne solution serait de l'examiner.

Si vous ne souhaitez pas utiliser OSGI, une implémentation possible pourrait être d'utiliser une instance de la classe JarClassloader pour chaque fichier JAR.

Et créez une nouvelle classe MultiClassloader qui étend Classloader. Cette classe aurait en interne un tableau (ou une liste) de JarClassloaders, et dans la méthode defineClass () itérerait à travers tous les chargeurs de classe internes jusqu'à ce qu'une définition puisse être trouvée, ou qu'une NoClassDefFoundException soit levée. Quelques méthodes d'accès peuvent être fournies pour ajouter de nouveaux JarClassloaders à la classe. Il existe plusieurs implémentations possibles sur le net pour un MultiClassLoader, vous n'aurez donc peut-être même pas besoin d'écrire la vôtre.

Si vous instanciez un MultiClassloader pour chaque connexion au serveur, il est en principe possible que chaque serveur utilise une version différente de la même classe.

J'ai utilisé l'idée de MultiClassloader dans un projet, où les classes contenant des scripts définis par l'utilisateur devaient être chargées et déchargées de la mémoire et cela fonctionnait très bien.

Mario Ortegón
la source
31
Notez également que selon java.sun.com/docs/books/jls/second_edition/html/... le déchargement des classes est une optimisation et, selon l'implémentation de la JVM, peut ou non avoir lieu.
5
En tant qu'alternative plus simple et légère à OSGi, essayez les modules JBoss - chargement de classe modulaire avec chargeur de classe par module (un groupe de fichiers JAR).
Ondra Žižka
42

Oui, il existe des moyens de charger des classes et de les "décharger" plus tard. L'astuce consiste à implémenter votre propre chargeur de classe qui réside entre le chargeur de classe de haut niveau (le chargeur de classe System) et les chargeurs de classe du (des) serveur (s) d'application, et d'espérer que les chargeurs de classe du serveur d'application délèguent le chargement de classe aux chargeurs supérieurs. .

Une classe est définie par son package, son nom et le chargeur de classe qu'elle a initialement chargé. Programmez un classloader "proxy" qui est le premier chargé au démarrage de la JVM. Flux de travail:

  • Le programme démarre et la vraie classe "principale" est chargée par ce chargeur de classe proxy.
  • Chaque classe qui est alors normalement chargée (c'est-à-dire pas via une autre implémentation de chargeur de classe qui pourrait briser la hiérarchie) sera déléguée à ce chargeur de classe.
  • Le chargeur de classe proxy délègue java.xet sun.xau chargeur de classe système (ceux-ci ne doivent pas être chargés via un autre chargeur de classe que le chargeur de classe système).
  • Pour chaque classe remplaçable, instanciez un chargeur de classe (qui charge réellement la classe et ne la délègue pas au chargeur de classe parent) et chargez-le via celui-ci.
  • Stockez le package / nom des classes sous forme de clés et le chargeur de classe sous forme de valeurs dans une structure de données (c'est-à-dire Hashmap).
  • Chaque fois que le chargeur de classe proxy reçoit une requête pour une classe qui a été chargée auparavant, il renvoie la classe à partir du chargeur de classe stocké auparavant.
  • Il devrait suffire de localiser le tableau d'octets d'une classe par votre chargeur de classe (ou de "supprimer" la paire clé / valeur de votre structure de données) et de recharger la classe au cas où vous voudriez la changer.

Fait juste là ne devrait pas venir une ClassCastException ou LinkageError etc.

Pour plus d'informations sur les hiérarchies des chargeurs de classes (oui, c'est exactement ce que vous implémentez ici; -) regardez "Server-Based Java Programming" par Ted Neward - ce livre m'a aidé à implémenter quelque chose de très similaire à ce que vous voulez.

Georgi
la source
3
Je ne comprends pas les gens qui ont coché -1 pour cette réponse sans laisser de commentaire. Pour moi, ça a l'air bien. Peut-être en avoir un ClassLoaderpar classe est un peu trop, un ClassLoaderpar JAR a du sens. Pourrait être plus précis sur la façon de forcer le téléchargement de classe dans le schéma proposé? Par exemple, comment puis-je garantir que les instances de classes chargées par ClassLoaderA ne sont pas référencées par des instances chargées par ClassLoaderB?
dma_k
@dma_k exactement, la réponse est bonne, mais cela ne touche pas aux points clés que vous avez mentionnés.
zinking
@Georgi existe-t-il une implémentation existante que nous pouvons installer / réutiliser pour cela?
Traîneau
1
Ce serait très très utile, si vous pouviez fournir l'exemple de code java.Pour être précis, je cherche Comment décharger des classes à l'aide de CustomClassLoader mais je n'ai pas eu de chance.
Sriharsha grv
@ Sriharshag.rv Avez-vous essayé ceci et implémenté un exemple?
niaomingjian
17

J'ai écrit un classloader personnalisé, à partir duquel il est possible de décharger des classes individuelles sans GCing le classloader. Chargeur de classe Jar

Kamran
la source
Fonctionne comme un charme :). Existe-t-il une méthode pour décharger tous les fichiers de classe d'un fichier jar?
Ercksen
Malheureusement pas pour le moment. Mais examinera cela. Peut-être dans les prochaines versions.
Kamran
Au fait, j'ai trouvé une petite solution de contournement. Si vous en avez un JarClassLoaderpour chaque fichier jar que vous avez chargé, vous pouvez l'appeler getLoadedClasses(), puis parcourir chacun d'eux et le décharger.
Ercksen
12

Les chargeurs de classe peuvent être un problème délicat. Vous pouvez particulièrement rencontrer des problèmes si vous utilisez plusieurs chargeurs de classe et que leurs interactions ne sont pas clairement et rigoureusement définies. Je pense que pour pouvoir décharger une classe, vous devrez supprimer toutes les références à toutes les classes (et leurs instances) que vous essayez de décharger.

La plupart des gens qui ont besoin de faire ce type de chose finissent par utiliser OSGi . OSGi est vraiment puissant et étonnamment léger et facile à utiliser,

Steve g
la source
7

Vous pouvez décharger un ClassLoader mais vous ne pouvez pas décharger des classes spécifiques. Plus précisément, vous ne pouvez pas décharger les classes créées dans un ClassLoader qui n'est pas sous votre contrôle.

Si possible, je suggère d'utiliser votre propre ClassLoader afin que vous puissiez décharger.

Jason Cohen
la source
4

Les classes ont une référence forte implicite à leur instance ClassLoader, et vice versa. Ils sont récupérés comme avec les objets Java. Sans accéder à l'interface des outils ou similaire, vous ne pouvez pas supprimer des classes individuelles.

Comme toujours, vous pouvez avoir des fuites de mémoire. Toute référence forte à l'une de vos classes ou chargeur de classe fera fuir le tout. Cela se produit avec les implémentations Sun de ThreadLocal, java.sql.DriverManager et java.beans, par exemple.

Tom Hawtin - ligne de pêche
la source
-1

Si vous regardez en direct si le déchargement de la classe a fonctionné dans JConsole ou quelque chose du genre , essayez également d'ajouter java.lang.System.gc()à la fin de votre logique de déchargement de classe. Il déclenche explicitement Garbage Collector.

Aleksander Drozd
la source
3
Attention: System.gc () n'appelle pas nécessairement GC. Il demande seulement au jvm de le démarrer, mais il ne l'applique pas. Et IME, il ne démarre souvent pas le GC: - \
Juh_