Come liberare memoria in Java?


146

C'è un modo per liberare memoria in Java, simile alla free()funzione di C ? Oppure impostare l'oggetto su null e fare affidamento su GC l'unica opzione?


151
Ok ... chiariamo una cosa. Solo perché pensi che qualcosa sia una cattiva pratica e non qualcosa da incoraggiare a fare, non lo rende degno di un voto negativo. Questa è una domanda chiara e valida, che chiede se esiste un modo per liberare memoria in Java senza fare affidamento sulla garbage collection. Sebbene possa essere scoraggiato e generalmente non utile o una buona idea, non puoi sapere che non ci sono scenari in cui potrebbe essere richiesto senza sapere ciò che Felix conosce. Felix potrebbe non aver nemmeno intenzione di usarlo. Potrebbe solo voler sapere se è possibile. In nessun modo merita un voto contrario.
Daniel Bingham,

7
Per chiarimenti, si rivolge a chiunque abbia votato verso il basso, non necessariamente commenti precedenti.
Daniel Bingham,

Risposte:


96

Java utilizza la memoria gestita, quindi l'unico modo per allocare memoria è usando l' newoperatore, e l'unico modo per poter deallocare la memoria è affidarsi al garbage collector.

Questo white paper sulla gestione della memoria (PDF) può aiutare a spiegare cosa sta succedendo.

Puoi anche chiamare System.gc()per suggerire che Garbage Collector venga eseguito immediatamente. Tuttavia, Java Runtime prende la decisione finale, non il codice.

Secondo la documentazione Java ,

La chiamata al metodo gc suggerisce che la Java Virtual Machine impieghi sforzi per riciclare oggetti inutilizzati al fine di rendere disponibile la memoria che occupano attualmente per un rapido riutilizzo. Quando il controllo ritorna dalla chiamata del metodo, Java Virtual Machine ha fatto il possibile per recuperare spazio da tutti gli oggetti scartati.


5
Forza l'esecuzione del Garbage Collector. Tuttavia non lo costringe a liberare memoria ...
Pablo Santa Cruz,

13
No Pablo, non forza il GC a funzionare.
Jesper,

1
Mi è stato detto da una persona molto affidabile che tutti i garbage collector di HotSpotVM ignorano del System.gc()tutto.
Esko,

1
Su winXp java SE GC esegue ogni System.gc () o quasi tutti, ma il documento API non lo garantisce.
Teodozjan,

2
@Pablo Santa Cruz Cosa vuoi dire che non libera memoria? L'ho appena testato sul mio programma che sembrava avere una perdita e l'utilizzo dell'ariete sembrava stabilizzarsi? E Daniel ha appena detto che suggerisce solo allora come mai la percentuale di montone usata si è sempre stabilizzata ogni volta che ho chiamato il metodo. Voi mi state confondendo.

65

Nessuno sembra aver menzionato l'impostazione esplicita di riferimenti a oggetti null, che è una tecnica legittima per "liberare" la memoria che potresti voler prendere in considerazione.

Ad esempio, supponiamo che tu abbia dichiarato List<String>a all'inizio di un metodo che è cresciuto in dimensioni per essere molto grande, ma è stato richiesto solo fino a metà del metodo. A questo punto è possibile impostare il riferimento Elenco su nullper consentire al garbage collector di potenzialmente recuperare questo oggetto prima del completamento del metodo (e comunque il riferimento non rientra nell'ambito).

Si noti che raramente utilizzo questa tecnica in realtà, ma vale la pena considerare quando si tratta di strutture di dati molto grandi.


8
Se stai davvero facendo un sacco di lavoro su un oggetto che viene utilizzato solo per parte di un metodo, ti suggerisco di farlo; il tuo metodo è troppo compilato, spezza il metodo nelle porzioni prima e dopo o usa un blocco per la prima metà del codice (il secondo è più utile per gli script di test)
Peter Lawrey

5
Il luogo in cui l'impostazione di un riferimento a un oggetto su null è importante è quando fa riferimento a un altro oggetto di lunga durata (o eventualmente a una var statica). Ad esempio, se si dispone di un array longevo di oggetti di grandi dimensioni e si smette di utilizzare uno di tali oggetti, è necessario impostare il riferimento di array su null per rendere l'oggetto disponibile per GC.
Hot Licks

22
System.gc(); 

Esegue il Garbage Collector.

La chiamata al metodo gc suggerisce che la Java Virtual Machine impieghi sforzi per riciclare oggetti inutilizzati al fine di rendere disponibile la memoria che occupano attualmente per un rapido riutilizzo. Quando il controllo ritorna dalla chiamata del metodo, la Java Virtual Machine ha fatto il possibile per recuperare spazio da tutti gli oggetti scartati.

Non consigliato.

Modifica: ho scritto la risposta originale nel 2009. Ora è il 2015.

I raccoglitori di immondizia sono migliorati costantemente negli ultimi 20 anni circa di Java. A questo punto, se stai chiamando manualmente il Garbage Collector, potresti prendere in considerazione altri approcci:

  • Se stai forzando GC su un numero limitato di macchine, può valere la pena avere un punto di bilanciamento del carico lontano dalla macchina corrente, in attesa che finisca di servire i client connessi, il timeout dopo un certo periodo di tempo per sospendere le connessioni e quindi solo difficile -vvia JVM. Questa è una soluzione terribile, ma se stai guardando System.gc (), il riavvio forzato potrebbe essere un possibile punto di svolta.
  • Prendi in considerazione l'utilizzo di un altro Garbage Collector. Ad esempio, il (nuovo negli ultimi sei anni) G1 è un modello a pausa ridotta; utilizza complessivamente più CPU, ma fa del suo meglio per non forzare mai un arresto improvviso nell'esecuzione. Poiché le CPU dei server ora hanno quasi tutte core multipli, questo è un compromesso davvero disponibile.
  • Guarda le tue bandiere che sintonizzano l'uso della memoria. Soprattutto nelle versioni più recenti di Java, se non si hanno così tanti oggetti in esecuzione a lungo termine, considerare di aumentare le dimensioni di newgen nell'heap. newgen (giovane) è dove vengono allocati nuovi oggetti. Per un server web, tutto ciò che è stato creato per una richiesta viene messo qui e, se questo spazio è troppo piccolo, Java impiegherà più tempo ad aggiornare gli oggetti in memoria a vita più lunga, dove sono più costosi da uccidere. (Se newgen è leggermente troppo piccolo, lo pagherai.) Ad esempio, in G1:
    • XX: G1NewSizePercent (il valore predefinito è 5; probabilmente non importa).
    • XX: G1MaxNewSizePercent (il valore predefinito è 60; probabilmente aumenta questo.)
  • Valuta di dire al garbage collector che non stai bene con una pausa più lunga. Ciò causerà corse GC più frequenti, per consentire al sistema di mantenere il resto dei suoi vincoli. In G1:
    • XX: MaxGCPauseMillis (il valore predefinito è 200.)

1
Commentando il mio post, questo spesso non fa nulla e chiamarlo ripetutamente può rendere instabile la JVM e quant'altro. Potrebbe anche investire il tuo cane; avvicinati con cautela.
Decano J

1
Darei grande enfasi alla parte "suggerisce" di "Chiamare il metodo gc suggerisce che lo sforzo di espansione di JVM"
matt b

2
@Jesper, la risposta di Dean afferma "suggerisce". In effetti ha pubblicato la documentazione esatta dal javadocs del metodo ...
matt b

2
@Software Monkey: Sì, avrei potuto modificarlo. Ma dato che Dean J era ovviamente attivo (pubblicando solo pochi minuti fa), ho pensato che fosse una cortesia chiedergli di farlo. Se non lo avesse fatto, sarei tornato qui e fatto la modifica ed eliminato il mio commento.
Daniel Pryden,

1
Vale anche la pena dire PERCHÉ non è raccomandato. Se JVM presta attenzione al "suggerimento" di eseguire il GC, quasi sicuramente rallenterà la tua app, probabilmente di molti ordini di grandezza!
Stephen C,

11

* "Mi affido personalmente alle variabili di annullamento come segnaposto per la futura corretta eliminazione. Ad esempio, mi prendo il tempo di annullare tutti gli elementi di un array prima di eliminare effettivamente (rendendo nullo) l'array stesso."

Questo non è necessario. Il modo in cui funziona il GC Java è che trova oggetti che non hanno alcun riferimento a loro, quindi se ho un oggetto x con un riferimento (= variabile) che lo punta, il GC non lo cancellerà, perché c'è un riferimento a quell'oggetto:

a -> x

Se si annulla a quanto segue:

a -> null
     x

Quindi ora x non ha un riferimento che lo punta e verrà eliminato. La stessa cosa accade quando si imposta a per fare riferimento a un oggetto diverso da x.

Quindi se hai un array arr che fa riferimento agli oggetti x, ye z e una variabile a che fa riferimento all'array sembra che:

a -> arr -> x
         -> y
         -> z

Se si annulla a quanto segue:

a -> null
     arr -> x
         -> y
         -> z

Quindi il GC trova arr come non avere alcun riferimento impostato su di esso e lo elimina, il che ti dà questa struttura:

a -> null
     x
     y
     z

Ora il GC trova x, yey e li elimina pure. Annullare ogni riferimento nell'array non migliorerà nulla, utilizzerà solo il tempo e lo spazio della CPU nel codice (detto questo, non danneggerà ulteriormente di così. Il GC sarà comunque in grado di funzionare come dovrebbe ).


5

Un motivo valido per voler liberare memoria da qualsiasi programma (java o no) è rendere disponibile più memoria ad altri programmi a livello di sistema operativo. Se la mia applicazione java utilizza 250 MB, potrei volerlo forzare a 1 MB e rendere i 249 MB disponibili per altre app.


Se hai bisogno di liberare esplicitamente un pezzo di 249 MB, in un programma Java, la gestione della memoria non sarebbe la prima cosa su cui vorrei lavorare.
Marc DiMillo,

3
Ma liberare spazio di archiviazione all'interno dell'heap Java non (nel caso generale) non lo rende disponibile per altre app.
Hot Licks

5

Per estendere la risposta e il commento di Yiannis Xanthopoulos e Hot Licks (scusate, non posso ancora commentare!), È possibile impostare le opzioni VM come questo esempio:

-XX:+UseG1GC -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30

Nel mio jdk 7 questo rilascerà quindi la memoria VM inutilizzata se oltre il 30% dell'heap diventa libero dopo GC quando la VM è inattiva. Probabilmente dovrai mettere a punto questi parametri.

Anche se non l'ho visto enfatizzato nel link qui sotto, nota che alcuni garbage collector potrebbero non obbedire a questi parametri e di default java potrebbe sceglierne uno per te, se ti capita di avere più di un core (quindi l'argomento UseG1GC sopra ).

Argomenti VM

Aggiornamento: per Java 1.8.0_73 ho visto JVM rilasciare occasionalmente piccole quantità con le impostazioni predefinite. Sembra farlo solo se ~ 70% dell'heap è inutilizzato però .. non so se sarebbe più aggressivo rilasciando se il sistema operativo avesse poca memoria fisica.


4

Ho fatto esperimenti su questo.

È vero che System.gc();suggerisce solo di eseguire Garbage Collector.

Ma chiamare System.gc();dopo aver impostato tutti i riferimenti null, migliorerà le prestazioni e l'occupazione della memoria.


Penso che non si possa dire con certezza che "chiamando System.gc (); dopo aver impostato tutti i riferimenti su null, migliorerà le prestazioni e l'occupazione della memoria". Perché esiste un'enorme complessità computazionale di System.gc (). E anche dopo aver chiamato System.gc () e aver effettivamente raccolto la spazzatura, il jvm potrebbe non restituire la memoria al sistema operativo o al sistema. JVM potrebbe conservare la memoria per riferimento futuro. Vedere questo risposta .
Md. Abu Nafee Ibna Zahid,

3

Se vuoi veramente allocare e liberare un blocco di memoria puoi farlo con ByteBuffer diretti. C'è anche un modo non portatile per liberare la memoria.

Tuttavia, come è stato suggerito, solo perché devi liberare memoria in C, non significa che sia una buona idea farlo.

Se ritieni di avere davvero un buon caso d'uso gratuito (), ti preghiamo di includerlo nella domanda in modo che possiamo vedere cosa stai facendo, è molto probabile che ci sia un modo migliore.


3

Interamente da javacoffeebreak.com/faq/faq0012.html

Un thread a bassa priorità si occupa automaticamente della garbage collection per l'utente. Durante il tempo di inattività, il thread può essere richiamato e può iniziare a liberare memoria precedentemente allocata a un oggetto in Java. Ma non preoccuparti: non cancellerà i tuoi oggetti su di te!

Quando non ci sono riferimenti a un oggetto, questo diventa un gioco equo per il garbage collector. Invece di chiamare una routine (come gratuita in C ++), devi semplicemente assegnare tutti i riferimenti all'oggetto su null o assegnare una nuova classe al riferimento.

Esempio :

public static void main(String args[])
{
  // Instantiate a large memory using class
  MyLargeMemoryUsingClass myClass = new MyLargeMemoryUsingClass(8192);

  // Do some work
  for ( .............. )
  {
      // Do some processing on myClass
  }

  // Clear reference to myClass
  myClass = null;

  // Continue processing, safe in the knowledge
  // that the garbage collector will reclaim myClass
}

Se il codice sta per richiedere una grande quantità di memoria, è possibile richiedere al garbage collector di iniziare a recuperare spazio, anziché consentirgli di farlo come thread a bassa priorità. Per fare ciò, aggiungi quanto segue al tuo codice

System.gc();

Il garbage collector tenterà di recuperare spazio libero e l'applicazione potrà continuare a essere eseguita, con quanta più memoria possibile recuperata (su alcune piattaforme potrebbero applicarsi problemi di frammentazione della memoria).


1

Nel mio caso, poiché il mio codice Java dovrebbe essere trasferito in altre lingue nel prossimo futuro (principalmente C ++), almeno voglio pagare il servizio labiale per liberare memoria in modo adeguato, in modo da aiutare il processo di porting in seguito.

Personalmente mi affido alle variabili di annullamento come segnaposto per la corretta eliminazione futura. Ad esempio, mi prendo il tempo di annullare tutti gli elementi di un array prima di eliminare effettivamente (rendendo nullo) l'array stesso.

Ma il mio caso è molto particolare e so che sto facendo dei successi quando lo faccio.


1

* "Ad esempio, supponiamo che tu abbia dichiarato un Elenco all'inizio di un metodo che è cresciuto in dimensioni per essere molto grande, ma era richiesto solo fino a metà del metodo. A questo punto puoi impostare il riferimento Elenco su null per consentire al garbage collector di potenzialmente recuperare questo oggetto prima del completamento del metodo (e comunque il riferimento non rientra nell'ambito). " *

Questo è corretto, ma questa soluzione potrebbe non essere generalizzabile. Quando si imposta un riferimento all'oggetto Elenco su null -will- renderà disponibile la memoria per la garbage collection, questo vale solo per un oggetto Elenco di tipi primitivi. Se l'oggetto Elenco contiene invece tipi di riferimento, l'impostazione dell'oggetto Elenco = null non farà alcuna differenza tra i tipi di riferimento contenuti nell'elenco. In questo caso, l'impostazione di List object = null renderà orfani i tipi di riferimento contenuti i cui oggetti non saranno disponibili per la garbage collection a meno che l'algoritmo garbage collection non sia abbastanza intelligente da determinare che gli oggetti sono rimasti orfani.


1
Questo in realtà non è vero. Il Garbage Collector Java è abbastanza intelligente da gestirlo correttamente. Se si annulla l'Elenco (e gli oggetti all'interno dell'Elenco non hanno altri riferimenti ad essi), il GC può recuperare tutti gli oggetti all'interno dell'Elenco. Potrebbe scegliere di non farlo al momento, ma alla fine li recupererà. Lo stesso vale per i riferimenti ciclici. Fondamentalmente, il modo in cui funziona il GC è cercare esplicitamente oggetti orfanizzati e poi recuperarli. Questo è l'intero lavoro di un GC. Il modo in cui lo descrivi renderebbe un GC completamente inutile.
Dakkaron,

1

Inoltre java fornisce la garbage collection automatica a volte vorrai sapere quanto è grande l'oggetto e quanto ne rimane. Memoria libera usando programmaticamente import java.lang;e Runtime r=Runtime.getRuntime(); per ottenere valori di memoria usando mem1=r.freeMemory();per liberare la memoria chiama il r.gc();metodo e la chiamatafreeMemory()


1

La raccomandazione di JAVA è di assegnare a null

Da https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

L'assegnazione esplicita di un valore nullo a variabili non più necessarie consente al garbage collector di identificare le parti di memoria che possono essere recuperate in modo sicuro. Sebbene Java fornisca la gestione della memoria, non impedisce perdite di memoria o l'utilizzo di quantità eccessive di memoria.

Un'applicazione può indurre perdite di memoria non rilasciando riferimenti a oggetti. In questo modo si impedisce al garbage collector Java di recuperare quegli oggetti e si traduce in un aumento della quantità di memoria utilizzata. Annullare esplicitamente i riferimenti alle variabili dopo il loro utilizzo consente al garbage collector di recuperare la memoria.

Un modo per rilevare perdite di memoria è utilizzare strumenti di profilazione e acquisire istantanee di memoria dopo ogni transazione. Un'applicazione priva di perdite in stato stazionario mostrerà una memoria heap attiva costante dopo le garbage collection.

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.