2015-10-16 16 views
8

Questo SO answer chiarisce alcune cose sul flag JVM -Xmx. Cercando di sperimentare ho fatto la seguente:è -Xmx un limite rigido?

import java.util.List; 
import java.util.ArrayList; 

public class FooMain { 

    private static String memoryMsg() { 
     return String.format("%s. %s. %s" 
          , String.format("total memory is: [%d]",Runtime.getRuntime().totalMemory()) 
          , String.format("free memory is: [%d]",Runtime.getRuntime().freeMemory()) 
          , String.format("max memory is: [%d]",Runtime.getRuntime().maxMemory())); 
    } 

    public static void main(String args[]) { 
     String msg = null; 
     try { 
      System.out.println(memoryMsg()); 
      List<Object> xs = new ArrayList<>(); 
      int i = 0 ; 
      while (true) { 
       xs.add(new byte[1000]); 
       msg = String.format("%d 1k arrays added.\n%s.\n" 
            , ++i 
            , memoryMsg()); 
      } 
     } finally { 
      System.out.printf(msg); 
     } 
    } 
} 

compilarlo con javac FooMain.java. Quando eseguo con una dimensione massima mucchio di 5 milioni di byte ottengo:

java -Xmx5000000 FooMain 
total memory is: [5242880]. free memory is: [4901096]. max memory is: [5767168] 
4878 1k arrays added. 
total memory is: [5767168]. free memory is: [543288]. max memory is: [5767168]. 
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded 
      at java.lang.String.toCharArray(String.java:2748) 
      at java.util.Formatter$FormatSpecifier.print(Formatter.java:3048) 
      at java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:2744) 
      at java.util.Formatter$FormatSpecifier.print(Formatter.java:2702) 
      at java.util.Formatter.format(Formatter.java:2488) 
      at java.util.Formatter.format(Formatter.java:2423) 
      at java.lang.String.format(String.java:2792) 
      at FooMain.memoryMsg(FooMain.java:7) 
      at FooMain.main(FooMain.java:21) 

Anche se i numeri sono abbastanza vicini, non sembrano molto preciso (con l'eccezione del total memory alla fine raggiungere esattamente il max memory). In particolare 5368 matrici di 1000 byte ciascuna dovrebbero richiedere più di 5000000 o anche 5242880 byte. Come dovrebbero essere compresi questi numeri?

Questa è la Java che sto utilizzando:

java version "1.7.0_80" 
Java(TM) SE Runtime Environment (build 1.7.0_80-b15) 
Java HotSpot(TM) Server VM (build 24.80-b11, mixed mode) 
+0

Forse Garbage Collection è stato avviato nel frattempo ... –

+1

@RobertNiestroj non è possibile poiché tutti i riferimenti sono mantenuti –

+1

@RobertNiestroj perché dovrebbe essere qualcosa GCed? Sto aggiungendo gli array in un 'List'. Nessun riferimento va oltre lo scopo. –

risposta

5

è -Xmx un limite rigido?

Dipende da cosa si intende per "difficile". Se intendi "non può essere modificato dal programma" allora Sì. Se si intende "preciso", quindi No. Le dimensioni dei vari spazi che il garbage collector utilizza è un multiplo di una potenza di 2 byte. E a quanto pare, la JVM arrotonda verso l'alto piuttosto che verso il basso.

Esempio di programma che assegna 1000 array di byte

Come dovrebbe questi numeri essere compreso?

Un array Java 1000 byte non occupa esattamente 1000 byte:

  • C'è un oggetto di intestazione, incluso lo spazio per un length campo a 32 bit. (In genere 3 x 32 bit in totale).

  • I nodi di heap sono allocati in multipli di 2^N byte.(In genere 2^3 == 8)


allora si ha il problema di cosa sta causando il OutOfMemoryError. Potresti pensare che sia perché l'heap è completamente pieno. Tuttavia, in questo caso il messaggio dice "Limite di sovraccarico del GC superato". Ciò significa che la JVM ha rilevato che la JVM sta spendendo una percentuale troppo grande del tempo complessivo della CPU che esegue il garbage collector. Questo è ciò che ha ucciso il GC ... non ha esaurito la memoria.

La logica del "limite di sovraccarico del GC" è che quando un heap si chiude completamente, il GC viene eseguito di frequente e riesce a recuperare sempre meno memoria ogni volta. Quando entri in una "spirale della morte" del GC, è meglio staccare la spina velocemente piuttosto che permettere all'applicazione di raggiungere il suo ultimo punto di rottura.

In ogni caso ... ciò significa che la tua euristica per capire quanta memoria è allocata quando lo heap è pieno probabilmente non è corretta.

6

Guardando il codice sorgente di OpenJDK, la logica per determinare la dimensione massima heap è piuttosto complesso, ed è determinato da un sacco di variabili. La dimensione effettiva heap occupa hotspot/src/share/vm/memory/collectorPolicy.cpp, esso utilizza il valore -Xmx fornito come input, e allinea l'alto, utilizzando il codice seguente:

align_size_up(MaxHeapSize, max_alignment()); 

align_size_up è definito come:

#define align_size_up_(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1)) 

E max_alignment è il prodotto delle dimensioni della pagina della memoria virtuale e della dimensione della scheda di raccolta dei dati inutili JVM. La dimensione della pagina VM è 4096 byte e la dimensione della scheda è 512 byte. Il collegamento di questi valori fornisce un MaxHeapSize effettivo di 6324224 byte, che corrisponderebbe bene ai numeri visualizzati.

Nota: ho solo guardato brevemente nel codice JVM, quindi è possibile che mi sia sfuggito qualcosa, ma la risposta sembra sommarsi a quello che stai vedendo.

1

La tua domanda è praticamente risposto da altre persone, ormai, ma per interesse ho fatto la matematica :-)

Si afferma here:

Questo valore deve un multiplo di 1024 maggiore di 2 MB.

5000000 non è un multiplo di 1024. La mia ipotesi era che l'argomento fosse arrotondato a un multiplo di 1024, che confermano le altre risposte. La tua memoria totale (che è più del solo spazio heap dichiarato) è 5767168 che è 5632 * 1024. È stato ingrandito durante il runtime dal 5242880 iniziale per adattarsi allo spazio heap in crescita. Notare che -Xmx dichiara il massimo, quindi non viene necessariamente assegnato immediatamente. Con l'aiuto di this source possiamo indicativi l'uso della memoria (assumendo 64bit):

  • 4878000 byte per byte
  • 4878 * 24 = 117072 byte gamma testa
  • pochi byte per gli altri oggetti creati e le stringhe
  • un po 'di fluttuazione della memoria a causa della garbage collection (almeno la stringa che crei ogni iterazione potrebbe essere buttata via)

S o gli array occupano 4995072 byte che (casualmente?) sarebbero già un multiplo di 1024. Ma c'è ancora il sovraccarico per gli altri oggetti. Quindi lo spazio heap è qualche multiplo di 1024 maggiore di 4995072 e inferiore a 5767168. Considerando lo spazio libero alla fine, questo ci lascia con 228808 byte per il resto dell'heap e l'utilizzo della memoria non heap, che suona come un numero plausibile.

Infine una parola sullo spazio libero rimasto alla fine. La JVM non riempie necessariamente tutta la memoria prima di bloccarsi. Se la memoria è quasi esaurita, la garbage collection viene eseguita più frequentemente. Se questo occupa una certa percentuale del runtime complessivo, the JVM exits, che è accaduto nel tuo caso.

Problemi correlati