Domanda

A volte uno dei nostri programmi sta ricevendo un errore OutOfMemory sul computer di un utente, ma ovviamente non quando lo sto testando. L'ho appena eseguito con JProfiler (con una licenza di valutazione di 10 giorni perché non l'ho mai usato prima) e filtrando il nostro prefisso di codice, il blocco più grande sia per dimensioni totali che per numero di istanze è di oltre 8000 istanze di una particolare classe semplice .

Ho fatto clic su " Garbage Collect " su JProfiler e la maggior parte dei casi di altre nostre classi è andata via, ma non quelle particolari. Ho eseguito di nuovo il test, sempre nella stessa istanza, e ha creato più di 4000 istanze della classe, ma quando ho fatto clic su " Garbage Collect " ;, quelle sono andate via lasciando le 8000+ originali.

Queste istanze restano bloccate in varie Collezioni in varie fasi. Presumo che il fatto che non siano spazzatura raccolti deve significare che qualcosa sta trattenendo un riferimento a una delle raccolte, quindi questo è trattenuto su un riferimento agli oggetti.

Qualche suggerimento su come posso capire cosa trattiene il riferimento? Sto cercando suggerimenti su cosa cercare nel codice, nonché i modi per scoprirlo in JProfiler se ci sono.

È stato utile?

Soluzione

Scarica l'heap e ispezionalo.

Sono sicuro che ci sia più di un modo per farlo, ma qui è semplice. Questa descrizione è per MS Windows, ma è possibile eseguire passaggi simili su altri sistemi operativi.

  1. Installa JDK se non lo possiedi già. Viene fornito con un sacco di strumenti accurati.
  2. Avvia l'applicazione.
  3. Apri Task Manager e trova l'ID processo (PID) per java.exe (o qualunque eseguibile che stai utilizzando). Se i PID non sono mostrati per impostazione predefinita, usa Visualizza > Seleziona Colonne ... per aggiungerle.
  4. Scarica l'heap usando jmap .
  5. Avvia il server jhat sul file generato e apri il browser su http: // localhost: 7000 (la porta predefinita è 7000). Ora puoi sfogliare il tipo che ti interessa e informazioni come il numero di istanze, cosa ha riferimenti a esse, eccetera.

Ecco un esempio:

C:\dump>jmap -dump:format=b,file=heap.bin 3552

C:\dump>jhat heap.bin
Reading from heap.bin...
Dump file created Tue Sep 30 19:46:23 BST 2008
Snapshot read, resolving...
Resolving 35484 objects...
Chasing references, expect 7 dots.......
Eliminating duplicate references.......
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

Per interpretarlo, è utile comprendere alcuni dei nomenclatura del tipo di array Java utilizza - come sapere che class [Ljava.lang.Object; significa davvero un oggetto di tipo Object [] .

Altri suggerimenti

Prova Eclipse Memory Analyzer. Ti mostrerà per ogni oggetto come è collegato a una radice GC - un oggetto che non è garbage collection perché è detenuto da JVM.

Vedi http://dev.eclipse.org/blogs/memoryanalyzer/2008/05/27/automated-heap-dump-analysis-finding-memory-leaks-with-one-click/ per ulteriori informazioni sul funzionamento di Eclipse MAT.

Vorrei esaminare le raccolte (specialmente quelle statiche) nelle tue classi (gli HashMap sono un buon punto di partenza). Prendi questo codice per esempio:

Map<String, Object> map = new HashMap<String, Object>(); // 1 Object
String name = "test";             // 2 Objects
Object o = new Object();          // 3 Objects
map.put(name, o);                 // 3 Objects, 2 of which have 2 references to them

o = null;                         // The objects are still being
name = null;                      // referenced by the HashMap and won't be GC'd

System.gc();                      // Nothing is deleted.

Object test = map.get("test");    // Returns o
test = null;

map.remove("test");               // Now we're down to just the HashMap in memory
                                  // o, name and test can all be GC'd

Fintanto che HashMap o qualche altra raccolta ha un riferimento a quell'oggetto, non sarà immondizia.

Nessun proiettile d'argento lì, devi usare il profiler per identificare raccolte che contengono quegli oggetti non necessari e trovare la posizione nel codice in cui avrebbero dovuto essere rimossi. Come ha detto JesperE, le raccolte statiche sono il primo posto da guardare.

Un candidato ovvio sono gli oggetti con finalizzatori. Possono indugiare mentre viene chiamato il loro metodo finalize. Devono essere raccolti, quindi finalizzati (di solito con un solo thread finalizzatore) e quindi raccolti di nuovo.

Inoltre, tieni presente che puoi ottenere un OOME perché gc non è riuscito a raccogliere memoria sufficiente, nonostante ci sia effettivamente abbastanza per la creazione dell'oggetto richiesta. Altrimenti le prestazioni si rovinerebbero al suolo.

Tieni d'occhio i contenitori statici. Qualsiasi oggetto in un contenitore statico rimarrà finché la classe sarà caricata.

Modifica: rimossa l'osservazione errata su WeakReference.

Ho appena letto un articolo su questo, ma mi dispiace non ricordare dove. Penso che potrebbe essere stato nel libro "Efficace Java". Se trovo il riferimento, aggiornerò la mia risposta.

Le due importanti lezioni che ha delineato sono:

1) I metodi finali indicano a gc cosa fare quando elimina l'oggetto, ma non gli chiede di farlo, né esiste un modo per richiederlo.

2) L'equivalente moderno della "perdita di memoria" in ambienti di memoria non gestiti, sono i riferimenti dimenticati. Se non hai impostato tutti i riferimenti a un oggetto su null quando hai finito, l'oggetto non non sarà mai eliminato. Questo è molto importante quando si implementa il proprio tipo di raccolta o il proprio wrapper che gestisce una raccolta. Se hai un pool o uno stack o una coda e non imposti il ??bucket su null quando " rimuovi " un oggetto dalla raccolta, il bucket in cui si trovava l'oggetto manterrà tale oggetto in vita fino a quando quel bucket non è impostato per fare riferimento a un altro oggetto.

disclaimer: so che sono state menzionate altre risposte, ma sto cercando di offrire maggiori dettagli.

Ho usato il profiler Yourkit Java ( http://www.yourkit.com ) per le prestazioni ottimizzazioni su java 1.5. Ha una sezione su come lavorare sulle perdite di memoria. Lo trovo utile.

http://www.yourkit.com/docs /75/help/performance_problems/memory_leaks/index.jsp

Puoi ottenere una valutazione di 15 giorni: http: //www.yourkit .com / download / YJP-7.5.7.exe

BR,
~ A

Le raccolte sono già state menzionate. Un altro percorso difficile da trovare è se si utilizzano più ClassLoader, poiché il vecchio classloader potrebbe non essere in grado di essere raccolto in modo inutile fino a quando tutti i riferimenti non sono andati.

Controlla anche le statistiche: sono brutte. I framework di registrazione possono tenere aperte le cose che possono contenere riferimenti nelle appendici personalizzate.

Hai risolto il problema?

Alcuni suggerimenti:

  • Mappe illimitate utilizzate come cache, soprattutto se statiche
  • ThreadLocals nelle app del server, perché i thread di solito non muoiono, quindi ThreadLocal non viene liberato
  • Interning stringhe (Strings.intern ()), che risulta in una pila di stringhe nel PermSpace

Se ricevi errori OOM in un linguaggio garbage collection, di solito significa che c'è un po 'di memoria che non viene presa in considerazione dal raccoglitore. Forse i tuoi oggetti contengono risorse non Java? in tal caso, dovrebbero avere una sorta di metodo "close" per assicurarsi che la risorsa venga rilasciata anche se l'oggetto Java non viene raccolto abbastanza presto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top