Perché le dimensioni dell'heap sono fisse su JVM?


20

Qualcuno può spiegarmi perché i JVM (non ne ho controllati troppi, ma non ne ho mai visto uno che non lo ha fatto in quel modo) devono essere eseguiti su una dimensione heap fissa? So che è più semplice implementarlo su un semplice heap contiguo, ma Sun JVM ha ormai più di un decennio, quindi mi aspetto che abbiano avuto il tempo di migliorarlo.

La necessità di definire la dimensione massima della memoria del programma al momento dell'avvio sembra una cosa del 1960, quindi ci sono le cattive interazioni con la gestione della memoria virtuale del sistema operativo (GC che recupera i dati scambiati, incapacità di determinare quanta memoria è il processo Java usando davvero dal lato del sistema operativo, enormi quantità di spazio VM sprecato (lo so, non ti importa delle tue fantasiose macchine a 48 bit ...)). Immagino anche che i vari tristi tentativi di costruire piccoli sistemi operativi all'interno della JVM (server di applicazioni EE, OSGi) siano almeno in parte responsabili di questa circostanza, perché l'esecuzione di più processi Java su un sistema porta invariabilmente a sprecare risorse perché è necessario dare a ciascuno di loro la memoria che potrebbe essere necessario utilizzare al massimo.

Sorprendentemente, Google non ha prodotto le tempeste di indignazione per ciò che mi sarei aspettato, ma potrebbero essere state seppellite da milioni di persone che hanno scoperto le dimensioni dell'heap fisso e accettandolo per un dato di fatto.


La progettazione dei server delle applicazioni EE avrebbe perfettamente senso anche senza questa "circostanza" in quanto la stessa JVM ha bisogno di spazio e il passaggio da un thread a un altro è più economico del passaggio da un processo all'altro: questa è una delle cose che hanno reso Java grande alla fine degli anni '90.
Michael Borgwardt,

Questo è gentile, ho una vaga idea e mi chiedo quanto ci abbia pensato. Ad esempio, come cambierebbe l'interazione GC / swap se non avessi un limite di heap? Come viene sprecato lo spazio della VM? Ottieni il tuo spazio 2 / 3Gb sia che lo usi o meno, e se stai spingendo i limiti di quello spazio non importa se hai un heap fisso o mobile. Del resto, in che modo più JVM sprecano qualcosa di diverso dallo swap (che dovrebbe essere configurato in modo appropriato per lo scopo della macchina)?
kdgregory,

2
È un po 'strano, ma è informato da anni di esperienza quotidiana nella scrittura e nel funzionamento di una piattaforma basata su Java. Se non esegui lo scambio (perché renderà il tuo sistema non rispondente per 20 minuti fino a quando il processo di fuga non esaurisce lo spazio per allocare) e l'overcommit della memoria è disattivato per motivi di stabilità (il killer OOM non è molto bravo a scegliere le vittime ), ti interessa lo spazio della VM e, contrariamente a quanto sottintendono le persone sottostanti, l'avvio di una VM Java con -Xmx2048m assegnerà immediatamente 2 GB di memoria virtuale (almeno nel mio Sun JVM su Linux) per un programma con una variabile.
themel

Ottima domanda Mi chiedevo la stessa cosa. Ma quale dei "fatti" presentati qui in q e le risposte sono corrette?
Martin Ba,

Per le reazioni che stavi cercando, vai semplicemente al localizzatore di insetti solari ... ad es. Qui , qui e qui . Leggi quelli e prova la rabbia indignata :)
Basic

Risposte:


23

Tui hai torto. La dimensione dell'heap delle JVM non è fissa, ma limitata:

  • -Xmx imposta la dimensione massima della memoria dell'heap
  • -Xms imposta la dimensione minima della memoria dell'heap

L'impostazione di un limite superiore è necessaria per diversi motivi. Innanzitutto, indica al garbage collector quando intervenire. In secondo luogo, impedisce a JVM di ostruire l'intera macchina consumando troppa memoria. La dimensione minima dell'heap è probabilmente utile per riservare la quantità di memoria necessaria almeno al programma, per evitare che si esaurisca la memoria (perché altri processi consumano troppo).


9
no il minimo è l'impostazione predefinita per evitare un avvio lento quando molto deve essere allocato con conseguente aumento dell'heap ripetuto, il paging si occuperà dell'esaurimento fisico del ram
maniaco del cricchetto

@ratchetfreak che era la mia seconda ipotesi ;-)
user281377 il

@ user281377 In questo caso, come può C # funzionare correttamente senza una dimensione massima della memoria dell'heap?
cmorse

cmorse: posso solo indovinare. Forse Java è destinato a server di grandi dimensioni, dove molte applicazioni condividono risorse e sono auspicabili limiti rigorosi, mentre .net è piuttosto realizzato per PC e server più piccoli e dedicati.
user281377

@ user281377: Le applicazioni Java che ho usato che esauriscono lo spazio dell'heap generalmente lo gestiscono in modo molto scadente, in genere solo dopo un arresto anomalo o in seguito molto instabile. E ASP.net funziona sia su server grandi che piccoli. Quello che davvero non capisco è perché, per impostazione predefinita, Java impone questo limite. Mi piacerebbe sapere dal ragionamento alla base della loro decisione ... Sono sicuro che avevano una buona ragione.
cmorse

6

Immagino che la risposta abbia qualcosa a che fare con l'eredità di Java. È stato originariamente progettato come un linguaggio da utilizzare per i sistemi embedded, dove ovviamente le risorse sono limitate e non si desidera che i processi divorino semplicemente tutto ciò che è disponibile. Aiuta anche con l'amministrazione del sistema, in quanto semplifica il provisioning delle risorse su un server se è possibile impostare limiti di risorse. Vedo che le JVM più recenti sembrano utilizzare più heap non continui, anche se ovviamente tutto appare come un unico heap nel tuo codice.

(FWIW, dovevi specificare i requisiti di memoria di un programma nelle versioni MacOS pre-Darwin di Apple [fino al System 7, che era l'ultimo che ho usato], che era ben negli anni '80.)


1
+1 - per essere (1) l'unica risposta che ha effettivamente risposto alla domanda e (2) essere plausibile.
kdgregory,

2

È necessario fornire al GC un meccanismo per indicare quando eseguirlo, altrimenti il ​​programma riempirà l'intero spazio di memoria virtuale. Esistono molti modi alternativi per attivare GC: tempo trascorso, numero di allocazioni, numero di assegnazioni, probabilmente altri a cui non riesco a pensare in questo momento. IMO nessuno di questi è buono come semplicemente impostare un limite di memoria ed eseguire GC quando lo spazio allocato raggiunge quel limite.

La chiave è impostare i limiti corretti . Considero -ms"questa è la quantità di memoria necessaria alla mia app" e -mxcome "non dovrebbe mai superare questa quantità". In uno schieramento di produzione, i due dovrebbero essere vicini se non uguali e dovrebbero essere basati su reali requisiti misurati.

Le tue preoccupazioni sulla memoria virtuale "sprecata" sono fuori posto: è virtuale, è (quasi) gratuita. Sì, assegnare un heap troppo grande significa che non puoi avviare tanti thread o caricare tanti file mappati in memoria. Ma fa parte del design dell'applicazione: hai scarse risorse, devi dividerle in un modo che consenta l'esecuzione dell'applicazione. In un heap "C-style", che si espanderà fino a quando non raggiungi la parte superiore della memoria, il problema di base è lo stesso, non devi pensarci finché non sei nei guai.

L'unica cosa che un grande mucchio potrebbe "sprecare" è lo spazio di swap, poiché tutti i segmenti scrivibili richiedono un impegno da swap. Ma questo fa parte della progettazione del sistema: se vuoi avere un sacco di JVM in esecuzione sulla stessa scatola, aumenta lo scambio o riduci la loro assegnazione di heap. Se iniziano a battere, allora stai provando a fare troppo con il sistema; acquista più memoria (e se stai ancora eseguendo un processore a 32 bit, acquista una nuova scatola).


1
Ma la soglia per l'esecuzione di GC non ha nulla a che fare con una soglia fissa che l'utilizzo totale di memoria da parte del programma non può superare. (In effetti probabilmente non dovrebbe; se hai un grande mucchio e puoi solo GC quando è pieno, hai necessariamente periodi rari ma lunghi di GC). Vedi la raccolta dei rifiuti generazionale.
Ben

@Ben - sì, hai ragione. E la mia seconda frase sottolinea che ci sono alternative. Non sono tuttavia d'accordo sul fatto che un heap di dimensioni fisse sia il modo sbagliato di gestire GC nel caso generale. Una JVM correttamente sintonizzata utilizza la dimensione heap "giusta"; nella mia esperienza, lunghe pause GC si verificano quando la JVM non è stata ottimizzata correttamente. E spesso la dimensione "giusta" dell'heap è molto più piccola di quanto si pensi.
kdgregory,

-2

Come dice user281377, si specifica solo un limite massimo di quanta memoria può consumare il processo . Naturalmente, l'applicazione stessa afferrerà solo lo spazio di cui ha bisogno.

Se esiste o meno un limite massimo predefinito è un'altra domanda, sia con pro che contro.

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.