2010-07-16 14 views
5

Stiamo eseguendo una piccola applicazione Web scritta JRuby su Rails in esecuzione su Tomcat. Stiamo utilizzando un back-end di Spring condiviso con un'altra applicazione web di produzione. Sfortunatamente, continuiamo a imbattersi in problemi PermGen.Rintracciare il problema PermGen con JRuby su Rails in Tomcat

OS: Ubuntu Linux 2.6.24-24-server di # 1 SMP x86_64 GNU/Linux Java: 1.6.0_21 Tomcat: 6.0.28 JRuby: 1.5.0 Rails: 2.3.7

Al momento siamo sottoposti a scansione da Google, Yahoo e Baidu, pertanto l'utilizzo del sito è scaduto. Ho monitorato Tomcat con JConsole e stiamo sicuramente riscontrando un problema con un numero eccessivo di classi. All'avvio di Tomcat, abbiamo circa 12.000 classi caricate. Dopo 8 ore, abbiamo caricato quasi 75.000 classi. PermGen va da 100 MB a 460 MB nello stesso tempo.

Lo scaricamento della classe sta funzionando, ma ha scaricato solo ~ 500 classi nello stesso periodo di 8 ore. PermGen non sembra mai essere raccolto.

stiamo correndo con le seguenti opzioni VM per Tomcat:

-Xms2048m -Xmx2048m -XX:MaxPermSize=512m -XX:PermSize=128m \ 
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:ParallelGCThreads=4 \ 
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled 

Ovviamente c'è un qualche tipo di perdita. La domanda è come dove? Qualche consiglio su come rintracciare chi e cosa è responsabile di questo? Spero che sia un errore davvero stupido da parte nostra, ma non sono sicuro da dove cominciare.

Qualsiasi consiglio sarebbe molto apprezzato.

EDIT

Sembra che stiamo vedendo una nuova classe creata per ogni singola richiesta in arrivo.

EDIT 2

è sicuramente legato alla JRuby. Usando JConsole, ho abilitato la modalità Verbose per il caricatore di classi. Ecco un esempio da catalina.out:

[Loaded anon_class1275113147_895127379 from file:/opt/apache-tomcat-6.0.28/webapps/notes/WEB-INF/lib/jruby-core-1.5.0.jar] 
[Loaded anon_class1354333392_895127376 from file:/opt/apache-tomcat-6.0.28/webapps/notes/WEB-INF/lib/jruby-core-1.5.0.jar] 
[Loaded anon_class1402528430_895127373 from file:/opt/apache-tomcat-6.0.28/webapps/notes/WEB-INF/lib/jruby-core-1.5.0.jar] 

Quindi la domanda diventa come faccio a rintracciare il responsabile per la creazione di quelle classi in più?

EDIT 3

Non sono sicuro se questo il problema, ma in qualche modo ci stiamo finendo con un numero folle di caricatori di classe. Ran jmap -permstat PID e ottenuto:

class_loader classes bytes  parent_loader alive?    type 
total = 1320 135748 947431296 N/A    alive=1, dead=1319 N/A 

Sembra un po 'eccessivo. La maggior parte sono uno dei tre tipi di classloader: sun.reflect.DelegatingClassLoader, org.jruby.util.JRubyClassLoader o org.jruby.util.ClassCache$OneShotClassLoader. Anche in questo caso, il campione di uscita dal jmap -permstat:

class_loader   classes bytes  parent_loader   alive? type 
0x00007f71f4e93d58  1  3128  0x00007f71f4d54680  dead sun/reflect/[email protected] 
0x00007f721e51e2a0  57103 316038936 0x00007f720431c958  dead org/jruby/util/[email protected] 
0x00007f72182f2b10  4  12944  0x00007f721d7f3030  dead org/jruby/util/[email protected] 
0x00007f721d7d50d8  9  457520  0x00007f720431c958  dead org/jruby/util/[email protected] 

risposta

6

PermGen è sicuramente un problema con le applicazioni basate su JRuby. Non sono sorpreso che il CMS non stia raccogliendo molto. In genere non c'è una vera perdita di memoria, ma l'applicazione è solo pesante e difficile su permgen e non è ancora stata livellata.

posso offrire alcune opzioni:

  1. Bump up PermGen ancora di più per vedere se si può trovare il punto di livellamento-off.
  2. Verificare se è possibile farla franca con l'esecuzione dell'applicazione in modalità pura-interpretata (-Djruby.compile.mode = OFF). Questo dovrebbe sbarazzarsi di una grossa fetta delle classi che riempiono il tuo permgen.
  3. Provare a eseguire con Rails 2.2 e maggiore la modalità threadsafe!. L'esecuzione dell'applicazione in un singolo runtime è un altro modo per ottenere grandi risparmi di memoria, e ciò vale anche per permgen.

MODIFICA: Cordiali saluti, questa domanda si è rivelata essere a JRuby bug. Le versioni 1.5.2 e 1.6 dovrebbero risolvere questo particolare problema. I miei commenti sopra rimangono in generale.

+0

75.000 classi per 4 istanze mi sembrano ancora molto ... :) Ma farò sicuramente qualche altro test. Grazie! – organicveggie

+0

La cosa che mi riguarda è che sembra che ogni nuova richiesta crei nuove classi che non vengono mai rilasciate. Questo non mi sembra giusto. Capisco perché JRuby sta creando nuove classi, ma non riesco a capire perché non vengono ripulite ... – organicveggie

+0

Come posso sapere se jruby.compile.mode = OFF funziona correttamente? Ho aggiunto -Djruby.compile.mode = OFF a CATALINA_OPTS in catalina.sh, ho avviato tomcat e lanciato un webcrawler contro il sito. JConsole indica che il conteggio totale della classe e il permgen totale sono in costante aumento. – organicveggie

1

ci sono strumenti di profiling, e le persone che sanno come usarli. Non sono uno di loro, temo.

Brute consiglio forza:

Riavviare il Tomcat ogni 8 ore. Il tempo di inattività totale visto dai tuoi utenti sarà molto accettabile.Problema risolto;)


EDIT

Oh, va bene! The boring solution.

+0

Grazie, ma il riavvio di Tomcat ogni 8 ore non è una soluzione accettabile. Soprattutto perché sono il ragazzo che lo farà. :) Il punto qui è risolvere il problema sottostante. – organicveggie

+0

'cron' e gli amici sono stati inventati quindi non è necessario riavviare manualmente. Hai un problema nel codice Ruby o nell'implementazione di JRuby o in una combinazione oscura di questo e di Tomcat, quindi non si tratta nemmeno di un problema di memoria Java "standard". Immagino che riavvierai il tuo server un sacco di volte prima di ripararlo, quindi sarebbe saggio esaminare gli script di riavvio regolari. –

+0

Assolutamente. Ma sono piuttosto a mio agio con il processo di arresto e avvio di tomcat e di automazione di quello. Lo abbiamo sotto controllo. Ma i tempi di fermo ogni 8 ore, anche se sono solo 30-60 secondi, non sono considerati accettabili per la gestione. Sai come va. :) Quindi la mia domanda qui è davvero alla ricerca di consigli su come tracciare la fonte del problema. – organicveggie

2

Abbiamo avuto un problema simile con un'applicazione web di Sinatra utilizzando JRuby 1.5.1: JVM L'opzione TraceClassLoading stampa anon_class * caricata insieme a ogni richiesta.

Dopo aver passato un po 'di tempo a restringere dove è stata caricata quella classe anonima, che viene eseguita stampando le istruzioni di traccia in console, abbiamo finalmente capito che era causato chiamando un metodo mancante su un oggetto Java.

Questa chiamata ha attivato JRuby per aggiungere il metodo mancante all'oggetto Java. Quel processo ha creato una nuova classe JRuby singleton, che è stata denominata "anon_class" seguita da alcuni valori hash. Poiché si tratta di un tipo di classe, rimane in PermGen e non viene mai raccolto da GC.

La soluzione alternativa è evitare di chiamare quel metodo mancante o fornire un'implementazione. Nel nostro caso, stavamo tentando di chiamare il metodo sort con un blocco su un oggetto ArrayList Java. Se prima invochiamo il metodo "to_a" per convertire l'ArrayList Java in array JRuby, quindi, l'ordinamento con un blocco non creerà un anon_class.

Quindi, vorrei suggerire di rivedere il codice per i luoghi che accedono all'oggetto Java da JRudy.

2

solo per fornire un semplice esempio per mostrare questo problema e soluzione:

require 'java' 
include_class java.util.ArrayList 

list = ArrayList.new 
list << 3 
list << 2 
list << 1 

3.times do 
    new_list = list.sort { |a, b| a <=> b} 
    #new_list = list.to_a.sort { |a, b| a <=> b} 
    puts new_list 
end 

Assumere il nome del file è test_classload.rb, i seguenti sono uscite: $ JRuby -J-XX: + TraceClassLoading test_classload.rb | grep anon_class
[anon_class819349464_307995535 Caricato da JVM_DefineClass]
[anon_class729155693_307995574 Caricato da JVM_DefineClass]
[anon_class1690464956_307995577 Caricato da JVM_DefineClass]

Se l'interruttore alla linea commentato, l'uscita è vuoto: no anon_class caricato.

Problemi correlati