2010-04-12 5 views
30

Ho letto da qualche parte che l'utilizzo delle istanze di classe come di seguito non è una buona idea in quanto potrebbero causare perdite di memoria. Qualcuno può dirmi se questa è una dichiarazione valida? O hanno qualche problema ad usarlo in questo modo?L'utilizzo dell'istanza di classe come chiave Mappa è una procedura consigliata?

Map<Class<?>,String> classToInstance = new HashMap(); 

classToInstance.put(String.class,"Test obj"); 
+0

Non sono sicuro che causerebbe perdite di memoria ... significa solo che la versione statica di String (utilizzata dai suoi metodi statici) sarà referenziata dalla tua mappa. – Powerlord

+1

I problemi di perdita di memoria sono (ad esempio) quando si appende un riferimento a un oggetto o una classe da una mappa di maggiore durata. Ad esempio, se in un'app Web si registra un driver JDBC dal proprio war (DriverManager.ergisterDriver), la classe DriverManager, caricata dal programma di caricamento del bootstrap (non dal classloader webapp), contiene un riferimento alla classe del driver. Pertanto, quando si scarica l'app Web, non è possibile scaricare il classloader webapp (a causa della classe di driver hooked) e nessuna delle altre classi di webapp viene scaricata. E non è carino – helios

+0

Sarei curioso di sapere dove hai sentito questo concetto di perdita? Riesci a ricordare qualche dettaglio su dove hai sentito questo? Era forse nel contesto di un ambiente con più classloader (ad esempio, contenitore di app web)? –

risposta

18

Sì, devi essere prudente! Ad esempio, se il codice è in esecuzione in un contenitore Web e si ha l'abitudine di eseguire una distribuzione a caldo di applicazioni Web, un riferimento mantenuto a un oggetto di classe singola può causare una perdita di memoria permgen significativa.

This article spiega il problema in dettaglio. Ma in poche parole, il problema è che ogni classe contiene un riferimento al suo programma di caricamento classi e ogni programma di caricamento classi contiene riferimenti a ogni classe che ha caricato. Quindi se una classe è raggiungibile, lo sono tutte.


Da Java 8 - PermGen è stato rimosso. Pensi che sia giusto usare l'istanza di classe come chiave HashMap in qualsiasi situazione?

Tenere presente che si verificherà ancora una perdita di memoria. Tutte le classi caricate dinamicamente utilizzate nella tua HashMap (chiave o valore) e (almeno) altre classi caricate dinamicamente saranno mantenute raggiungibili. Ciò significa che il GC non sarà in grado di scaricarli/eliminarli. Ciò che prima era una perdita di permgen è ora una normale perdita di heap.

+0

Da Java 8 - Permgen è stato rimosso. Pensi che sia giusto usare l'istanza di classe come chiave HashMap in qualsiasi situazione? Grazie! – Loc

5

No, non è un problema. Finché stavi creando un'istanza della classe in ogni caso, non stai usando più memoria tenendo un riferimento alla classe stessa.

+0

Interessante domanda. Posso vedere come non è necessaria memoria aggiuntiva qui, ma il riferimento a un oggetto di classe impedirà comunque che classToInstanceMap venga raccolto? –

+1

Non è un problema ... ad eccezione dell'istanza della mappa che pende da una classe o da un oggetto di vita più lunga rispetto alla classe utilizzata come chiave. In tal caso, la classe "chiave" non è raccolta con garbage perché la mappa è ancora lì ... Se la mappa ha vita breve, o almeno non è appesa a una classe base JVM, va bene. – helios

+2

@Chris Knight: No, la raggiungibilità va solo avanti. 'classToInstanceMap' avendo un riferimento a' String.class' impedirà che 'String.class' venga raccolto, ma non viceversa. 'String.class' potrebbe riferirsi a un blocco di informazioni sul tipo che non è idoneo per la raccolta anayway, ma non ne so abbastanza sulla JVM per dirlo con certezza. –

0

Come ha detto Stephen C, la perdita di memoria è dovuta ai classloader. Ma il problema è più acuto che a prima vista. Considerate questo:

mapkey --> class --> classloader --> all other classes defined by this classloader. 

Inoltre,

class --> any static members, including static Maps e.g. caches. 

Pochi tali cache statici possono iniziare ad aggiungere fino a gravi quantità di memoria persa ogni volta che una webapp o qualche altro modo dinamico (classloaded) app caricata è in bici.

Esistono diversi approcci per risolvere questo problema. Se non ti interessano le diverse "versioni" della stessa classe da diversi classloader, quindi semplicemente il tasto basato su Class.getName(), che è un java.lang.String.

Un'altra opzione è utilizzare java.util.WeakHashMap. Questa forma di mappa mantiene solo i riferimenti deboli alle chiavi. I riferimenti deboli non reggono il GC, quindi la loro chiave non causerà un accumulo di memoria. Tuttavia, i valori sono non con riferimento debolmente. Quindi se i valori sono ad esempio istanze delle classi utilizzate come chiavi, lo WeakHashMap non funziona.

0

Dipende da dove è definito il riferimento classToInstance=new HashMap();.

Una classe punta al suo programma di caricamento classe e, di conseguenza, il programma di caricamento classi non può essere sottoposto a garbage collection mentre esiste un riferimento alla classe. Ma se i riferimenti formano un cerchio (o un cluster irraggiungibile), questo funziona ancora: il GC sa come gestire i riferimenti circolari.

parent class loader --> class loader <--> class // no GC is possible 
parent class loader  class loader <--> class // circular references are GC'ed 

Quindi un riferimento a una classe può evitare che il caricatore di classe di essere GC'ed solo se i riferimenti provengono da un oggetto/classe in un genitore class loader.

parent class loader  class loader <--> class // no GC is possible 
        \-------------------/ 

Questo è ciò che la frase "qualsiasi riferimento dall'esterno dell'applicazione di un oggetto la cui applicazione la classe viene caricato dal classloader dell'applicazione causerà una perdita classloader" si intende nella article menzionato in Stephen C . risposta.

Ma non dovrebbe essere il caso se la mappa è parte della vostra applicazione.

Problemi correlati