2010-07-16 8 views
5

Estamos ejecutando una pequeña aplicación web escrita JRuby on Rails que se ejecuta bajo Tomcat. Estamos usando un back-end de Spring que se comparte con otra aplicación web de producción. Desafortunadamente, seguimos encontrando problemas con PermGen.Localiza el problema de PermGen con JRuby on Rails en Tomcat

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

Actualmente estamos siendo rastreados por Google, Yahoo y Baidu, por lo que el uso del sitio está activo. He estado monitorizando Tomcat con JConsole y definitivamente estamos viendo un problema con un número excesivo de clases. Cuando se lanza Tomcat, tenemos aproximadamente 12,000 clases cargadas. Después de 8 horas, tenemos casi 75,000 clases cargadas. PermGen va de 100MB a 460MB en el mismo tiempo.

La descarga de clases está funcionando, pero solo descargó ~ 500 clases en ese mismo período de 8 horas. PermGen nunca parece ser recolectado.

nos estamos quedando con las siguientes opciones de VM para Tomcat:

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

Obviamente hay algún tipo de fuga. La pregunta es ¿dónde? ¿Algún consejo sobre cómo rastrear quién y qué es responsable de esto? Espero que sea un error muy tonto de nuestra parte, pero no estoy seguro de por dónde empezar.

Cualquier consejo sería muy apreciado.

EDITAR

Parece que estamos viendo una nueva clase creada para cada solicitud entrante sola.

EDITAR 2

Es definitivamente relacionadas con JRuby. Utilizando JConsole, habilité el modo Verbose para el cargador de clases. He aquí una muestra de 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] 

Entonces la pregunta es ¿cómo puedo localizar a la parte responsable de la creación de esas clases adicionales?

EDITAR 3

No estoy seguro si este problema, pero de alguna manera estamos terminando con un número increíble de cargadores de clases. Ran jmap -permstat PID y obtuvo:

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

Eso parece un poco excesivo. La mayoría son uno de los tres tipos de cargadores de clase: sun.reflect.DelegatingClassLoader, org.jruby.util.JRubyClassLoader o org.jruby.util.ClassCache$OneShotClassLoader. Una vez más, muestra de salida de 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] 

Respuesta

6

PermGen es definitivamente un problema con las aplicaciones basadas en JRuby. No me sorprende que el CMS no esté recolectando mucho. Por lo general, no hay una verdadera pérdida de memoria, sino que la aplicación es pesada y dura en permgen y aún no se ha estabilizado.

que puede ofrecer algunas opciones:

  1. LEVANTACOLA PermGen aún más para ver si se puede encontrar el punto de nivelación.
  2. Compruebe si puede ejecutar su aplicación en modo de interpretación pura (-Djruby.compile.mode = OFF). Eso debería deshacerse de una gran parte de las clases que llenan tu permgen.
  3. Intenta ejecutar con Rails 2.2 y el modo mayor threadsafe!. Ejecutar su aplicación en un solo tiempo de ejecución es otra forma de obtener grandes ahorros de memoria, y eso también se aplica a Permgen.

EDIT: Para su información, esta pregunta resultó ser a JRuby bug. Las versiones 1.5.2 y 1.6 deberían solucionar este problema en particular. Mis comentarios anteriores siguen en pie en general.

+0

75,000 clases para 4 instancias todavía me parece mucho ... :) Pero definitivamente haré algunas pruebas más. ¡Gracias! – organicveggie

+0

Lo que me preocupa es que parece que cada nueva solicitud crea nuevas clases que nunca se publican. Eso simplemente no me parece bien. Entiendo por qué JRuby está creando nuevas clases, pero no puedo entender por qué no se limpian ... – organicveggie

+0

¿Cómo puedo saber si jruby.compile.mode = OFF funciona correctamente? Agregué -Djruby.compile.mode = OFF a CATALINA_OPTS en catalina.sh, inicié tomcat y lancé un webcrawler contra el sitio. JConsole indica que el recuento total de clases y el permgen total aumenta constantemente. – organicveggie

1

Hay herramientas de perfilado, y las personas que saben cómo usarlos. No soy uno de ellos, me temo.

Bruto consejos vigor:

Reinicie el Tomcat cada 8 horas. El tiempo de inactividad total como lo ven sus usuarios será muy aceptable.Problema resuelto;)


EDITAR

Oh, está bien! The boring solution.

+0

Gracias, pero reiniciar Tomcat cada 8 horas no es una solución aceptable. Especialmente porque soy el chico que hará eso. :) El punto aquí es resolver el problema subyacente. – organicveggie

+0

'cron' y amigos se inventaron por lo que no necesitaría reiniciar manualmente. Usted tiene un problema que está en su código Ruby o en la implementación de JRuby o una combinación oscura de eso y Tomcat, por lo que ni siquiera es un problema de memoria Java "estándar". Supongo que reiniciará su servidor muchas veces antes de que se solucione, por lo que sería aconsejable revisar los reinicios regulares de scripting. –

+0

Absolutamente. Pero estoy bastante cómodo con el proceso de detener y comenzar tomcat y automatizar eso. Lo tenemos bajo control. Pero el tiempo de inactividad cada 8 horas, incluso si solo son 30-60 segundos, no se considera aceptable para la administración. Tu sabes como va. :) Así que mi pregunta aquí es realmente buscar consejos sobre cómo rastrear el origen del problema. – organicveggie

2

Tuvimos un problema similar con una aplicación web Sinatra utilizando JRuby 1.5.1: La opción TraceClassLoading de JVM imprime anon_class * cargado junto con cada solicitud.

Después de pasar algún tiempo para delimitar dónde se cargó esa clase anónima, que se realiza imprimiendo instrucciones de seguimiento a la consola, finalmente descubrimos que se debió a la invocación de un método faltante en un objeto Java.

Esa llamada desencadenó que JRuby agregara ese método faltante al objeto Java. Ese proceso creó una nueva clase JRuby singleton, que se denominó "anon_class" seguida de algunos valores hash. Como es un tipo de clase, permanece en PermGen y GC nunca lo recopila.

La solución alternativa es evitar llamar a ese método faltante o proporcionar una implementación. En nuestro caso, intentamos llamar al método de clasificación con un bloque en un objeto Java ArrayList. Si llamamos primero al método "to_a" para convertir Java ArrayList a una matriz JRuby, entonces, ordenar con un bloque no creará una anon_class.

Por lo tanto, sugiero revisar el código de lugares que acceden al objeto Java desde JRudy.

2

sólo para proporcionar un ejemplo sencillo para mostrar este problema y la solución:

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 

asumir el nombre del archivo es test_classload.rb, los siguientes son los resultados: $ jruby -J-XX: + TraceClassLoading test_classload.rb | grep anon_class
[anon_class819349464_307995535 Cargado de JVM_DefineClass]
[anon_class729155693_307995574 Cargado de JVM_DefineClass]
[anon_class1690464956_307995577 Cargado de JVM_DefineClass]

Si el interruptor a la línea comentado, la salida está vacía: no anon_class cargado.

Cuestiones relacionadas