2011-09-20 19 views
7

Ho un caso in cui ho bisogno di creare molti caricatori di classe nella mia applicazione per rendere temporaneamente visibile del codice mentre gli script forniti dall'utente sono in esecuzione. Sto usando un URLClassLoader per questo e funziona abbastanza bene.Come si "chiude" un ClassLoader?

Quando termina lo script, voglio "scaricare" o "chiudere" il programma di caricamento classi per liberare le risorse.

È sufficiente impostare il riferimento al caricatore classe su null? Mi chiedo in particolare se alla fine mancherò i file handle perché le classi extra sono in file JAR.

PS: Deve funzionare con Java 5 e versioni successive. Sì, lo so ...

+0

Potrebbe questo essere utile: http: // StackOverflow. it/questions/148681/unloading-classes-in-java? – Thomas

+0

Si desidera rimuovere i riferimenti agli oggetti o scaricare una classe. Quindi sei preoccupato per il ternured o il permgen – ssedano

+0

In realtà, sono preoccupato per le connessioni aperte (ad esempio, InputStreams aperto per caricare risorse). –

risposta

4

Quando tutte le classi caricate dal caricatore di classi non hanno più riferimenti e tutti i riferimenti al programma di caricamento classe sono stati cancellati, il programma di caricamento classi e le classi caricate verranno eliminati come gruppo.

Si noti che questo dipende dall'avere impostato l'attributo JVM che consente di scaricare le classi non referenziate. È impostato per impostazione predefinita nella maggior parte degli ambienti, ma potrebbe non essere in alcuni casi incorporati.

[Si noti che è un compito non banale rimuovere i riferimenti a una classe. Qualsiasi altra classe che faccia riferimento ad essa, ovviamente, impedirà la rimozione. Quindi la classe deve essere caricata usando ClassLoader.findClass o qualcosa di simile.]

+0

Conosci l'attributo? – home

+0

No. È uno dei più oscuri e potrebbe non essere esternalizzato sulle JVM successive. –

+0

Questo è un lato della medaglia. Se cambio il JAR che contiene le classi, creo un nuovo classloader e carico tramite lo stesso URL, otterrò le nuove classi o ci sarà il caching? –

1

Non ci sono metodi close() nel caricatore di classi URL o in nessuna delle sue classi genitore, quindi sei sfortunato.

Il GC non dovrebbe gestirlo?

+0

D'accordo, basta smettere di fare riferimento al proprio ClassLoader (da parte di suo padre in particolare) e verrà raccolto, le sue classi seguiranno. – Guillaume

+0

@Guillaume - Non è facile. –

3

Se non si dispone più di classi (e oggetto) caricate da quel classloader e se non si mantiene alcun riferimento a tale programma di caricamento classi, verrà gestito automaticamente dal garbage collector.

0

Ho esteso URLClassLoader e ho creato un metodo basato su Java 7s. Volevo sviluppare il mio IRC bot sul mio iPad 2, così ho fatto ciò che era necessario. Ora il mio sistema di plugin è stabile su Java 6 e 7, evviva.

+4

cosa fa il metodo 'close()'? –

+0

(Scommetto che non rende il caricatore da collezione.) –

5

Un po 'in ritardo, ma spero che questo sia utile per coloro che vengono a questa domanda più tardi (come me).

Con Java 7, un metodo close() è stato aggiunto a URLClassLoader, che è esattamente ciò che l'OP chiedeva.

EDIT (grazie a @Hot Licks) : OK, quindi non è esattamente quello che il PO ha chiesto. Non libera tutte le risorse o rende le risorse e il caricatore da collezione. Impedisce semplicemente il caricamento di più risorse utilizzando il programma di caricamento classi. Lo fa, tuttavia, chiude il file jar caricato con lo URLClassLoader.

+0

Il metodo * close() * non rende il caricatore e le sue risorse raccoglibili, ma semplicemente impedisce il caricamento di più classi. –

+0

Grazie. Non sono sicuro di quanto mi sia mancato. Sembra, tuttavia, che chiuda il file jar, il che significa che il file jar può ora essere cancellato/modificato, ecc., Se ho capito bene? –

+0

Sì, implica che i file jar siano chiusi. –

3

Se non è possibile utilizzare Java7 ed è metodo close(), l'uso di riflessione per chiudere tutti gli archivi JAR aperte di un programma di caricamento classe, in questo modo:

public void close() { 
try { 
    Class clazz = java.net.URLClassLoader.class; 
    java.lang.reflect.Field ucp = clazz.getDeclaredField("ucp"); 
    ucp.setAccessible(true); 
    Object sun_misc_URLClassPath = ucp.get(this); 
    java.lang.reflect.Field loaders = 
     sun_misc_URLClassPath.getClass().getDeclaredField("loaders"); 
    loaders.setAccessible(true); 
    Object java_util_Collection = loaders.get(sun_misc_URLClassPath); 
    for (Object sun_misc_URLClassPath_JarLoader : 
     ((java.util.Collection) java_util_Collection).toArray()) { 
     try { 
     java.lang.reflect.Field loader = 
      sun_misc_URLClassPath_JarLoader.getClass().getDeclaredField("jar"); 
     loader.setAccessible(true); 
     Object java_util_jar_JarFile = 
      loader.get(sun_misc_URLClassPath_JarLoader); 
     ((java.util.jar.JarFile) java_util_jar_JarFile).close(); 
     } catch (Throwable t) { 
     // if we got this far, this is probably not a JAR loader so skip it 
     } 
    } 
} catch (Throwable t) { 
    // probably not a SUN VM 
} 
return; 
} 
+0

Sto usando java 1.6, ho forzato il GC completo per ripulire URLClassloader e ho visto ancora molti file FD di jar mostrati da 'lsof'. Il codice ha risolto il mio problema! – waltersu