2009-03-18 14 views
43

Al lavoro, abbiamo riscontrato un problema con le eccezioni "PermGen out of memory", con il capo del team che ha deciso che si trattava di un bug nella JVM, qualcosa relativo alla distribuzione a caldo del codice. Senza spiegare molti dettagli, ha sottolineato che la distribuzione a caldo è un "problema difficile", così difficile che anche .NET non lo fa ancora.Che cosa rende la "hot deployment" una "problematica"?

Ho trovato molti articoli che spiegano il dispiegamento a caldo dalla prospettiva a volo d'uccello, ma mancano sempre di dettagli tecnici. Qualcuno potrebbe indicarmi una spiegazione tecnica e spiegare perché la distribuzione a caldo è "un problema difficile"?

+0

Perché questo Wiki di comunità, tra l'altro? – Eddie

+2

Pensavo che potesse essere d'aiuto nel caso in cui avessi lasciato qualcosa di poco chiaro ... fallo di default, è una cattiva etichetta? –

+0

Divertente: ho trovato un think-o mentre rileggo proprio ora, grazie! –

risposta

58

Quando una classe viene caricata, vari dati statici relativi alla classe vengono memorizzati in PermGen. Finché esiste un riferimento live a questa istanza di classe, l'istanza della classe non può essere raccolta in modo garbage.

Credo che una parte del problema abbia a che fare con il fatto che il GC rimuova o meno le vecchie istanze di Class da perm gen o no. In genere, ogni volta che si distribuisce a caldo, le nuove istanze di classe vengono aggiunte al pool di memoria di PermGen e le vecchie, ora non utilizzate, in genere non vengono rimosse. Per impostazione predefinita, Sun JVM non esegue la garbage collection in PermGen, ma può essere abilitata con argomenti di comando opzionali "java".

Pertanto, se si impiega a caldo abbastanza volte, alla fine si esaurirà lo spazio PermGen.

Se la tua app Web non si spegne completamente quando non distribuita, ad esempio se lascia un thread in esecuzione, tutte le istanze di classe utilizzate da tale app Web verranno bloccate nello spazio PermGen. Si ridistribuisci e ora viene caricata un'altra ulteriore copia di tutte queste istanze di classe in PermGen. Si annulla la distribuzione e il thread continua, bloccando un altro set di istanze di classe in PermGen. Puoi ridistribuire e caricare un intero gruppo di copie ... e alla fine il tuo PermGen si riempie.

a volte è possibile risolvere questo problema:

  • Fornire argomenti del comando a un recente Sun JVM per consentire GC in PermGen e delle classi. Cioè: -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
  • Usando una JVM diversa che non utilizza una di dimensioni PermGen fisso o che fa GC su classi caricate

Ma questo aiuterà solo se la vostra applicazione web si spegne completamente e in modo pulito, non lasciare riferimenti in tempo reale a nessuna delle istanze di classe di qualsiasi classe caricata dai caricatori di classe per tale app Web.

Anche questo non risolverà necessariamente il problema, a causa di perdite del caricatore di classe. (In alcuni casi troppe stringhe interne.)

Controlla i seguenti collegamenti per ulteriori informazioni (i due in grassetto hanno dei diagrammi per illustrare parte del problema).

+1

Eddie - non è la ragione per mantenere le classi anche se ci sono già oggetti esistenti che si aggirano con la "vecchia" definizione di classe? –

+0

@Edie: la maggior parte dei collegamenti è correlata alla memoria piuttosto che all'architettura del classloader, il che sarebbe molto utile per capire perché la distribuzione a caldo è un problema. Il problema della memoria è una conseguenza dei workaround. – OscarRyz

+0

Solo una nota sull'esecuzione dei thread: quando ho lasciato un thread in sospeso per ri-distribuire per errore, nel momento in cui il thread si è svegliato ha generato un NoClassDefFound (le classi sono state annullate), e poi (ovviamente) è morto. Dopo la sua morte PermGen dovrebbe essere OK per essere GCed. –

6

Il problema in termini generali è il modello di sicurezza di Java che tenta in realtà di impedire che una classe che ha già caricato caricare di nuovo.

Ovviamente Java ha supportato il caricamento di classi dinamiche, ma è difficile il re-caricamento della classe.

È stato considerato dannoso (e per una buona ragione) che un'applicazione java in esecuzione sia stata iniettata con una nuova classe con codice dannoso. Ad esempio, un'implementazione java.lang.String cracking proveniente da Internet, che invece di creare una stringa, cancella un file casuale hile invocando il metodo length().

Quindi, il modo in cui Java è stato concepito (e presumo .NET CLR di conseguenza, perché era altamente "ispirato" in JVM) era quello di impedire a una classe già caricata di caricare di nuovo quella stessa VM.

Hanno offerto un meccanismo per ignorare questa "funzione". Classloader, ma ancora una volta le regole per i programmi di caricamento classe, dovevano chiedere il permesso al classloader "genitore" prima di provare a caricare una nuova classe, se il genitore ha già caricato la classe, la nuova classe viene ignorata.

Per esempio ho usato classloader che caricano a classi da LDAP o RDBMS

Distribuisci caldo diventa una necessità nel mondo Java, quando il server di applicazione è diventato mainstream per Java EE (e anche creare la necessità di contenitori micro come la primavera per evitare questo tipo di fardello).

Riavviare l'intero server applicazione dopo ogni compilazione unità chiunque pazzo.

server provider Così app, offrono questo caricatori di classe "custom" per aiutare l'implementazione a caldo, e l'utilizzo di un file di configurazione, che il comportamento, dovrebbe essere disattivata quando è impostato in produzione. Ma il compromesso è che devi usare tonnellate di memoria in fase di sviluppo. Quindi il modo migliore per farlo è riavviare ogni 3 - 4 distribuzioni.

Questo non accade con altri linguaggi progettati fin dall'inizio per caricare le loro classi.

In Ruby, ad esempio, è anche possibile aggiungere metodi a una classe in esecuzione, sovrascrivere un metodo in fase di esecuzione o persino aggiungere un singolo metodo a un oggetto specifico univoco.

Il compromesso in questi tipi di ambienti è, naturalmente, memoria e velocità.

Spero che questo aiuti.

EDIT

ho trovato questo prodotto qualche tempo fa che promette che ricarica è rendere il più semplice possibile. Non ho ricordato il collegamento quando ho scritto per la prima volta questa risposta, e lo so.

È JavaRebel from ZeroTurnaround

+0

che casino nella tua testa, eh? quindi java non è stato progettato per caricare le classi in fase di runtime, eh? è per questo che ha Class.forName() sin dall'inizio? –

+0

Questo non è ciò che Oscar ha scritto Vladimir. Ha scritto che Java non è stato progettato per sostituire le classi in fase di runtime. –

+0

ClassLoader class esiste dalla versione 1. Non progettato per sostituire le classi in fase di runtime? Inoltre, la sicurezza non ha nulla a che fare con PermGen OOM. Il limite di PermGen è solo un brutto dettaglio di implementazione di Sun JVM. –

3

Sun JVM ha PermGen spazio fisso, e alla fine è tutto consumato (sì, a quanto pare a causa di un bug nel codice di caricamento classe correlati) => OOM.

Se è possibile utilizzare JVM di un altro fornitore (ad esempio Weblogic), estende dinamicamente lo spazio PermGen, quindi non otterrete mai OOM relativo a permgen.

+0

Aspetta lì. Questo non è vero al 100%. Il problema della gemma perm è non solo legato alla JVM e alle sue librerie. Una cattiva applicazione può anche causare errori PermGen. Inoltre, espandendo dinamicamente PerGen è assolutamente diffrente da risolverlo. Aspetta abbastanza tempo in ogni VM e il problema accadrà. – Antonio

+1

corretto. Ancora, PermGen dinamico ti consente di sopravvivere a periodi di installazioni più hot. In Sun JVM capita spesso in caso di ridistribuzione n. 1: -E –

0

Quale versione di java stai usando? Ci sono stati errori in Early Sun 1.4.2, ma ha funzionato per molto tempo.
BTW, come farai a dare la notizia al tuo team leader? Sei il capo della squadra?

+0

In realtà è un nuovo problema dal 1.5. O almeno mi sono imbattuto in 1.5 solo. –

+0

Stiamo riscontrando questo problema con 1.6. Probabilmente "interromperò" il lead della mia squadra inviandogli un link a questa pagina :-P –

Problemi correlati