Classi di scarico in Java?


174

Ho un caricatore di classi personalizzato in modo che un'applicazione desktop possa avviare in modo dinamico il caricamento di classi da un AppServer con cui devo parlare. Lo abbiamo fatto poiché la quantità di barattoli necessari per farlo è ridicola (se volessimo spedirli). Abbiamo anche problemi di versione se non cariciamo dinamicamente le classi in fase di esecuzione dalla libreria AppServer.

Ora, ho appena riscontrato un problema in cui ho bisogno di parlare con due diversi AppServer e ho scoperto che, a seconda delle classi caricate per prime, potrei interrompere male ... C'è un modo per forzare lo scarico della classe senza uccidere effettivamente la JVM?

Spero che abbia senso


Hai un classloader per ogni vaso? In che modo gli archivi dei contenitori OSGI per scaricare classloader? Sembra che non ci sia API di scarico nella classe classloader?
hetaoblog,

Risposte:


190

L'unico modo in cui è possibile scaricare una classe è se il Classloader utilizzato è la garbage collection. Ciò significa che i riferimenti a ogni singola classe e al classloader stesso devono seguire la strada del dodo.

Una possibile soluzione al problema è disporre di un Classloader per ogni file jar e di un Classloader per ciascuno degli AppServer che delegano il caricamento effettivo delle classi a specifici classloader Jar. In questo modo, puoi puntare a versioni diverse del file jar per ogni server app.

Questo non è banale, però. La piattaforma OSGi si impegna a fare proprio questo, poiché ogni bundle ha un caricatore di classi diverso e le dipendenze vengono risolte dalla piattaforma. Forse una buona soluzione sarebbe quella di dargli un'occhiata.

Se non si desidera utilizzare OSGI, una possibile implementazione potrebbe essere quella di utilizzare un'istanza della classe JarClassloader per ogni file JAR.

E crea una nuova classe MultiClassloader che estende Classloader. Questa classe internamente avrebbe una matrice (o un elenco) di JarClassloader e nel metodo defineClass () eseguirà l'iterazione attraverso tutti i caricatori di classi interni fino a quando non sarà possibile trovare una definizione o verrà generata una NoClassDefFoundException. È possibile fornire un paio di metodi di accesso per aggiungere nuovi JarClassloader alla classe. Esistono diverse possibili implementazioni in rete per un MultiClassLoader, quindi potrebbe non essere nemmeno necessario scriverne uno proprio.

Se installi un MultiClassloader per ogni connessione al server, in linea di principio è possibile che ogni server utilizzi una versione diversa della stessa classe.

Ho usato l'idea di MultiClassloader in un progetto, in cui le classi che contenevano script definiti dall'utente dovevano essere caricate e scaricate dalla memoria e ha funzionato abbastanza bene.


31
Si noti inoltre che, secondo java.sun.com/docs/books/jls/second_edition/html/…, lo scaricamento delle classi è un'ottimizzazione e, a seconda dell'implementazione di JVM, può o meno verificarsi.

5
Come alternativa più semplice e leggera a OSGi, prova i moduli JBoss : caricamento delle classi modulare con classloader per modulo (un gruppo di vasetti).
Ondra Žižka,

42

Sì, ci sono modi per caricare le classi e "scaricarle" in un secondo momento. Il trucco è implementare il proprio caricatore di classi che risiede tra il caricatore di classi di alto livello (il caricatore di classi di sistema) e i caricatori di classi dei server delle app e sperare che i caricatori di classi del server delle app deleghino il caricamento delle classi ai caricatori superiori .

Una classe è definita dal suo pacchetto, dal suo nome e dal caricatore di classi che ha caricato originariamente. Programmare un classloader "proxy" che è il primo che viene caricato all'avvio di JVM. Flusso di lavoro:

  • Il programma si avvia e la vera classe "principale" viene caricata da questo classloader proxy.
  • Ogni classe che viene quindi normalmente caricata (ovvero non tramite un'altra implementazione del programma di caricamento classi che potrebbe interrompere la gerarchia) verrà delegata a questo programma di caricamento classe.
  • Il proxy classloader delega java.xe sun.xal classloader di sistema (questi non devono essere caricati attraverso un altro caricatore di classi diverso dal classloader di sistema).
  • Per ogni classe sostituibile, creare un'istanza di un programma di caricamento classi (che carica realmente la classe e non la delega al programma di caricamento classi principale) e caricarlo tramite questo.
  • Memorizza il pacchetto / nome delle classi come chiavi e il classloader come valori in una struttura di dati (es. Hashmap).
  • Ogni volta che il programma di caricamento classi proxy ottiene una richiesta per una classe che è stata caricata in precedenza, restituisce la classe dal programma di caricamento classe memorizzato in precedenza.
  • Dovrebbe essere sufficiente individuare l'array di byte di una classe dal proprio caricatore di classi (o "eliminare" la coppia chiave / valore dalla struttura dei dati) e ricaricare la classe nel caso in cui si desideri modificarla.

Fatto proprio non dovrebbe venire un ClassCastException o LinkageError ecc.

Per ulteriori informazioni sulle gerarchie del caricatore di classi (sì, questo è esattamente ciò che stai implementando qui; -) guarda "Programmazione Java basata su server" di Ted Neward - quel libro mi ha aiutato a implementare qualcosa di molto simile a quello che desideri.


3
Non capisco le persone, che hanno barrato -1 per questa risposta senza lasciare un commento. Per me sembra buono. Forse averne uno ClassLoaderper classe è un po 'troppo, uno ClassLoaderper JAR ha senso. Potrebbe essere più specifico su come forzare il caricamento della classe nello schema proposto? Ad esempio, come posso garantire che le istanze delle classi caricate da ClassLoaderA non facciano riferimento alle istanze caricate da ClassLoaderB?
dma_k,

@dma_k esattamente, la risposta è buona, ma non tocca i punti chiave che hai citato.
tintinnio il

@Georgi esiste un'implementazione esistente che possiamo installare / riutilizzare per questo?
Slitta

1
Sarebbe molto utile se tu potessi fornire il codice java di esempio. Per essere precisi, sto cercando Come scaricare le classi usando CustomClassLoader ma non ho avuto fortuna.
Sriharsha grv,

@ Sriharshag.rv Hai provato questo e implementato un campione?
niaomingjian,

17

Ho scritto un programma di caricamento classi personalizzato, dal quale è possibile scaricare singole classi senza GCing il programma di caricamento classi. Caricatore classe Jar


Funziona come un fascino :). Esiste un metodo per scaricare tutti i file di classe di un file jar?
Ercksen,

Purtroppo non al momento. Ma lo esaminerò. Potrebbe essere nelle versioni future.
Kamran,

A proposito, ho trovato un po 'di soluzione. Se ne hai uno JarClassLoaderper ogni file jar che hai caricato, puoi chiamarlo getLoadedClasses(), quindi scorrere su ciascuno di essi e scaricarlo.
Ercksen,

12

I classloader possono essere un problema complicato. In particolare, puoi riscontrare problemi se stai utilizzando più classloader e le loro interazioni non sono definite in modo chiaro e rigoroso. Penso che per poter effettivamente scaricare una classe che stai andando devi rimuovere tutti i riferimenti a tutte le classi (e le loro istanze) che stai cercando di scaricare.

Molte persone che hanno bisogno di fare questo tipo di cose finiscono per usare OSGi . OSGi è davvero potente, sorprendentemente leggero e facile da usare,


7

È possibile scaricare un ClassLoader ma non è possibile scaricare classi specifiche. Più specificamente, non è possibile scaricare le classi create in un ClassLoader che non è sotto il tuo controllo.

Se possibile, ti suggerisco di usare il tuo ClassLoader in modo da poter scaricare.


4

Le classi hanno un forte riferimento implicito alla loro istanza ClassLoader e viceversa. Sono rifiuti raccolti come con oggetti Java. Senza colpire l'interfaccia degli strumenti o simili, non è possibile rimuovere singole classi.

Come sempre puoi avere perdite di memoria. Qualsiasi riferimento forte a una delle tue classi o al caricatore di classi perderà il tutto. Ciò si verifica con le implementazioni Sun di ThreadLocal, java.sql.DriverManager e java.beans, ad esempio.


-1

Se stai guardando dal vivo se la classe di scarico ha funzionato in JConsole o qualcosa del genere, prova anche ad aggiungere java.lang.System.gc()alla fine della tua logica di scarico della classe. Attiva esplicitamente Garbage Collector.


3
Attenzione: System.gc () non è necessario chiamare GC. Chiede solo a jvm di avviarlo, ma non lo impone. E IME spesso non avvia il GC: - \
Juh_
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.