Come profilo di memoria in Java?


Sto ancora imparando le corde di Java, quindi mi dispiace se c'è una risposta ovvia a questo. Ho un programma che sta prendendo un sacco di memoria e voglio capire un modo per ridurne l'utilizzo, ma dopo aver letto molte domande SO ho l'idea di dover dimostrare dove si trova il problema prima di iniziare a ottimizzarlo.

Quindi ecco cosa ho fatto, ho aggiunto un punto di interruzione all'inizio del mio programma e l'ho eseguito, poi ho iniziato visualVM e ho fatto profilare la memoria(ho anche fatto la stessa cosa in netbeans solo per confrontare i risultati e sono gli stessi). Il mio problema è che non so come leggerli, ho ottenuto l'area più alta solo dicendo char[] e non riesco a vedere alcun codice o altro(il che ha senso perché visualvm si sta connettendo alla jvm e non riesco a vedere la mia fonte, ma netbeans non mi mostra anche la fonte come fa quando fa la profilazione della cpu).

Fondamentalmente quello che voglio sapere è quale variabile (e si spera più dettagli come in quale metodo) tutta la memoria viene utilizzata in modo da poter mettere a fuoco a lavorare lì. C'è un modo semplice per farlo? In questo momento sto usando eclipse e java per lo sviluppo (e ho installato visualVM e netbeans specificamente per la profilazione, ma sono disposto a installare qualsiasi altra cosa che ritieni possa portare a termine questo lavoro).

EDIT: Idealmente, sto cercando qualcosa che prenderà tutti i miei oggetti e li ordinerà per dimensione (in modo da poter vedere quale sta monopolizzando la memoria). Attualmente restituisce informazioni generiche come string[] o int [] ma voglio sapere quale oggetto è riferendosi a così posso lavorare per ottenere le sue dimensioni più ottimizzate.

Author: Error_404, 2012-04-11

5 answers

Le stringhe sono problematiche

Fondamentalmente in Java, i riferimenti String (cose che usano char[] dietro le quinte ) domineranno la maggior parte delle applicazioni business in termini di memoria. Il modo in cui vengono creati determina la quantità di memoria che consumano nella JVM.

Solo perché sono così fondamentali per la maggior parte delle applicazioni aziendali come tipo di dati, e sono anche uno dei più affamati di memoria. Questa non è solo una cosa Java, i tipi di dati String occupano molta memoria in praticamente ogni linguaggio e libreria di runtime, perché almeno sono solo array di 1 byte per carattere o peggio ( Unicode ) sono array di più byte per carattere.

Una volta, durante la profilazione dell'utilizzo della CPU su un'app Web che aveva anche una dipendenza Oracle JDBC, ho scoperto che StringBuffer.append() dominava i cicli della CPU di molti ordini di grandezza su tutte le altre chiamate di metodo combinate , molto meno qualsiasi altra chiamata a metodo singolo. Il driver JDBC ha fatto un sacco di String manipolazione, una sorta di compromesso di usare PreparedStatements per tutto.

Quello che ti preoccupa non puoi controllare, non direttamente comunque

Ciò su cui dovresti concentrarti è ciò che è nel tuo controllo, che è assicurarti di non tenere i riferimenti più a lungo del necessario e di non duplicare le cose inutilmente. Le routine di garbage collection in Java sono altamente ottimizzate e, se impari come funzionano i loro algoritmi, puoi assicurarti che il tuo programma si comporta nel modo ottimale per far funzionare quegli algoritmi.

La memoria Heap Java non è come la memoria gestita manualmente in altre lingue, tali regole non si applicano

Quelle che sono considerate perdite di memoria in altre lingue non sono la stessa cosa/causa principale di Java con il suo sistema di garbage collection.

Molto probabilmente in Java la memoria non viene consumata da un singolo oggetto uber che perde ( riferimento penzolante in altri ambienti ).

È molto probabilmente molte allocazioni più piccole a causa di StringBuffer/StringBuilder oggetti non dimensionati in modo appropriato alle prime istantanee e quindi costretti a far crescere automaticamente gli array char[] per contenere le successive chiamate append().

Questi oggetti intermedi possono essere trattenuti più a lungo del previsto dal garbage collector a causa dell'ambito in cui si trovano e di molte altre cose che possono variare in fase di esecuzione.

ESEMPIO: {[20] } il garbage collector può decidere che ci sono candidati, ma perché ritiene che ci sia ancora molta memoria da avere che potrebbe essere troppo costoso per eliminarli a quel punto nel tempo, e aspetterà che la pressione della memoria aumenti.

Il garbage collector è veramente buono ora, ma non è magico, se stai facendo cose degenerate, farà sì che non funzioni in modo ottimale. C'è molta documentazione su Internet sulle impostazioni del garbage collector per tutte le versioni delle JVM.

Questi gli oggetti non referenziati potrebbero non aver raggiunto il tempo in cui il garbage collector pensa di averne bisogno per essere eliminati dalla memoria, o potrebbero esserci riferimenti a loro detenuti da qualche altro oggetto ( List), ad esempio che non ti rendi conto che punta ancora a quell'oggetto. Questo è ciò che è più comunemente indicato come una perdita in Java, che è una perdita di riferimento più specificamente.

ESEMPIO: {[20] } Se sai che devi creare un 4K String usando un StringBuilder crea con new StringBuilder(4096); non il default, che è come 32 e inizierà immediatamente a creare spazzatura che può rappresentare molte volte ciò che pensi che l'oggetto dovrebbe essere dimensionato.

Puoi scoprire quanti tipi di oggetti sono istanziati con VisualVM, questo ti dirà cosa devi sapere. Non ci sarà una grande luce lampeggiante che punta a una singola istanza di una singola classe che dice: "Questo è il grande consumatore di memoria!", cioè a meno che non ci sia solo un'istanza di alcuni char[] in cui stai leggendo un file enorme, e questo non è possibile, perché molte altre classi usano char[] internamente; e quindi lo sapevi già.

Non vedo alcuna menzione di OutOfMemoryError

Probabilmente non hai un problema nel tuo codice, il sistema di garbage collection potrebbe non essere messo sotto pressione sufficiente per entrare e deallocare oggetti che tu pensi dovrebbe essere ripulito. Cosa si pensa che sia un problema probabilmente non lo è, a meno che il tuo programma non si blocchi con OutOfMemoryError. Questo non è C, C++, Objective-C o qualsiasi altro linguaggio / runtime di gestione della memoria manuale. Non puoi decidere cosa è in memoria o meno al livello di dettaglio che ti aspetti che dovresti essere in grado di farlo.

 24
Author: feeling abused and harassed, 2012-04-11 16:52:16

In JProfiler , si può prendere andare al walker heap e attivare la vista oggetti più grandi. Vedrai gli oggetti che conservano la maggior parte della memoria. La memoria "Mantenuta" è la memoria che verrà liberata dal garbage collector se si rimuove l'oggetto.

È quindi possibile aprire i nodi oggetto per visualizzare l'albero di riferimento degli oggetti conservati. Ecco una schermata della vista oggetto più grande:

inserisci qui la descrizione dell'immagine

Disclaimer: la mia azienda sviluppa JProfiler

 8
Author: Ingo Kegel, 2012-04-15 14:54:49

Consiglierei di catturare i dump dell'heap e di utilizzare uno strumento come Eclipse MAT che ti consente di analizzarli. Ci sono molti tutorial disponibili. Fornisce una vista dell'albero dominator per fornire informazioni sulle relazioni tra gli oggetti nell'heap. In particolare per quello che hai menzionato, la funzione" path to GC roots " di MAT ti dirà dove si fa riferimento alla maggior parte di quegli oggetti char[], String[] e int []. JVisualVM può anche essere utile in identificazione di perdite e allocazioni, in particolare utilizzando istantanee con tracce di stack di allocazione. Ci sono alcuni walk-through del processo di ottenere le istantanee e confrontarle per trovare il punto di allocazione.

 2
Author: Rob Tanzola, 2012-04-12 00:43:02

Java JDK viene fornito con JVisualVM nella cartella bin, una volta che il server delle applicazioni (ad esempio è in esecuzione) è possibile eseguire visualvm e collegarlo al localhost, che fornirà l'allocazione della memoria e consentirà di eseguire il dump dell'heap

inserisci qui la descrizione dell'immagine

Per passaggi più dettagliati su come abilitare: http://sysdotoutdotprint.com/technologies/java/6

 2
Author: mel3kings, 2018-04-04 05:18:23

Se si utilizza visualVM per controllare l'utilizzo della memoria, si concentra sui dati, non sui metodi. Forse i tuoi grandi dati char [] sono causati da molti valori di stringa? A meno che non si stia utilizzando la ricorsione, i dati non verranno da variabili locali. Quindi puoi concentrarti sui metodi che inseriscono elementi in strutture di dati di grandi dimensioni. Per scoprire quali affermazioni precise causano la tua "perdita di memoria", ti suggerisco inoltre

 0
Author: DaveFar, 2012-04-11 15:32:48