Déchargement des classes en Java?

J’ai un chargeur de classe personnalisé pour qu’une application de bureau puisse démarrer dynamicment le chargement de classes à partir d’un AppServer auquel j’ai besoin de parler. Nous l’avons fait car la quantité de bocaux nécessaires pour le faire est ridicule (si nous voulions les expédier). Nous avons également des problèmes de version si nous ne chargeons pas les classes dynamicment à l’exécution à partir de la bibliothèque AppServer.

Maintenant, je viens de rencontrer un problème où j’ai besoin de parler à deux différents AppServers et j’ai constaté que selon les classes que je charge en premier, je risque de me casser … Est-il possible de forcer le déchargement de la classe sans tuer la JVM?

J’espère que cela a du sens

La seule manière de décharger une classe est que le Classloader utilisé soit récupéré. Cela signifie que les références à chaque classe et au classloader lui-même doivent aller dans le sens du dodo.

Une solution possible à votre problème consiste à avoir un chargeur de classes pour chaque fichier jar et un chargeur de classes pour chacun des AppServers qui délèguent le chargement réel des classes à des chargeurs de classes 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 sortingvial, cependant. La plate-forme OSGi s’efforce de faire exactement cela, car chaque bundle possède un chargeur de classes différent et les dépendances sont résolues par la plate-forme. Une bonne solution serait peut-être de la regarder.

Si vous ne voulez 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 en interne aurait un tableau (ou une liste) de JarClassloaders, et dans la méthode defineClass (), itérerait tous les chargeurs de classe internes jusqu’à ce qu’une définition soit trouvée ou qu’une exception NoClassDefFoundException soit lancée. Deux méthodes d’access peuvent être fournies pour append de nouveaux JarClassloaders à la classe. Il existe plusieurs implémentations possibles sur le net pour un MultiClassLoader, de sorte que vous n’avez peut-être même pas besoin d’écrire le 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 assez bien.

Oui, il existe des moyens de charger les 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 classes du ou des serveurs de l’application, et à espérer que les chargeurs de classes du serveur d’applications 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 chargé à l’origine. Programmer un classloader “proxy” qui est le premier chargé lors du démarrage de la JVM. Workflow:

  • Le programme démarre et la vraie classe “principale” est chargée par ce chargeur de classe proxy.
  • Chaque classe qui est normalement chargée (c’est-à-dire pas via une autre implémentation de classloader qui pourrait briser la hiérarchie) sera déléguée à ce chargeur de classe.
  • Le java.x proxy délègue java.x et sun.x au sun.x du 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 classloader (qui charge réellement la classe et ne le délègue pas au classloader parent) et chargez-le par ce biais.
  • Stockez le package / nom des classes en tant que clés et le classloader en tant que valeurs dans une structure de données (par exemple, Hashmap).
  • Chaque fois que le classloader proxy reçoit une demande pour une classe qui a été chargée auparavant, il renvoie la classe du chargeur de classes stockée 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 si vous souhaitez la modifier.

Fait juste il ne devrait pas y avoir une ClassCastException ou LinkageError etc.

Pour plus d’informations sur les hiérarchies de chargeur de classes (oui, c’est exactement ce que vous implémentez ici -), regardez “Programmation Java sur serveur” de Ted Neward – ce livre m’a aidé à implémenter quelque chose de très similaire à ce que vous vouliez.

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

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 classes et que leurs interactions ne sont pas définies clairement et rigoureusement. Je pense que pour pouvoir réellement décharger une classe, vous devez supprimer toutes les références aux classes (et à leurs instances) que vous essayez de décharger.

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

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 pour pouvoir décharger.

Les classes ont une référence forte implicite à leur instance ClassLoader et inversement. Ils sont collectés comme avec les objects Java. Sans toucher l’interface des outils ou similaire, vous ne pouvez pas supprimer des classes individuelles.

Comme toujours, vous pouvez obtenir des memory leaks. Toute référence forte à l’une de vos classes ou de votre chargeur de classe fera toute la différence. Cela se produit avec les implémentations Sun de ThreadLocal, java.sql.DriverManager et java.beans, par exemple.

Si vous regardez en direct si le déchargement de classe a fonctionné dans JConsole ou quelque chose, essayez également d’append java.lang.System.gc() à la fin de votre logique de déchargement de classe. Il déclenche explicitement le garbage collector.