2009-03-18 14 views
43

En el trabajo, hemos estado teniendo un problema con las excepciones "PermGen out of memory", y el líder del equipo decidió que se trataba de un error en la JVM, algo relacionado con la implementación en caliente del código. Sin explicar muchos detalles, señaló que la implementación en caliente es un "problema difícil", tan difícil que incluso .NET aún no lo hace.¿Qué hace que la implementación en caliente sea un "problema difícil"?

Encontré una gran cantidad de artículos que explican el despliegue en caliente desde el punto de vista del pájaro, pero siempre carentes de detalles técnicos. ¿Alguien podría indicarme una explicación técnica y explicar por qué el despliegue en caliente es "un problema difícil"?

+0

¿Por qué esta Wiki de la comunidad, por cierto? – Eddie

+2

Pensé que podría ayudar en caso de que dejara algo poco claro ... ¿eso es por defecto, es una mala etiqueta? –

+0

Gracioso: encontré una idea-o mientras volvía a leer en este momento, ¡gracias! –

Respuesta

58

Cuando se carga una clase, se almacenan varios datos estáticos sobre la clase en PermGen. Mientras exista una referencia en vivo a esta instancia de clase, la instancia de clase no puede ser recolectada como basura.

Creo que parte del problema tiene que ver con si el GC debe o no eliminar instancias de Class antiguas de perm gen, o no. Normalmente, cada vez que se implementa en caliente, se agregan nuevas instancias de clase al grupo de memoria de PermGen, y las antiguas, que ahora no se usan, generalmente no se eliminan. De forma predeterminada, las JVM de Sun no ejecutarán la recolección de elementos no utilizados en PermGen, pero esto se puede habilitar con argumentos de comando "java" opcionales.

Por lo tanto, si realiza una implementación caliente suficientes veces, con el tiempo agotará su espacio PermGen.

Si su aplicación web no se cierra por completo cuando no desplegado - si se deja un hilo conductor, por ejemplo - a continuación, todas las instancias de la clase utilizados por esa aplicación web será depositado en el espacio PermGen. Usted vuelve a implementar y ahora tiene otra copia completa de todas estas instancias de Clase cargadas en PermGen. Usted anula la implementación y el hilo continúa, fijando otro conjunto de instancias de clase en PermGen. Reasigna y carga un conjunto completo de copias de red ... y, finalmente, su PermGen se llena.

A veces se puede solucionar este problema:

  • El suministro de argumentos de comandos a una reciente JVM de Sun para permitir GC en PermGen y de las clases. O sea: -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
  • Utilizando una JVM diferente que no emplean un tamaño PermGen o fijo que hace GC de las clases cargadas

Pero esto ayudará única si su aplicación web se apaga por completo y limpiamente, no dejando referencias en vivo a ninguna de las instancias de clase de ninguna clase cargada por los cargadores de clase para esa aplicación web.

Incluso esto no solucionará necesariamente el problema, debido a fugas del cargador de clase. (Además de demasiadas cadenas internas en algunos casos.)

Consulte los siguientes enlaces para obtener más información (los dos en negrita tienen diagramas agradables para ilustrar parte del problema).

+1

Eddie: ¿no es la razón por la que se mantienen las clases, porque ya hay objetos existentes que se vuelven locos con la definición de clase "anterior"? –

+0

@Edie: la mayoría de los enlaces están relacionados con la memoria en lugar de la arquitectura del cargador de clases, que sería más útil para comprender por qué el despliegue en caliente es un problema. El problema de memoria es una consecuencia de las soluciones. – OscarRyz

+0

Solo una nota sobre la ejecución de los hilos: cuando dejé un hilo para dormir por volver a desplegarlo por error, en el momento en que el hilo se levantó arrojó un NoClassDefFound (las clases no se desplegaron), y luego (por supuesto) murió. Después de su muerte, PermGen debería estar bien para recibir GC. –

6

El problema en términos generales es el modelo de seguridad de Java que en realidad intenta evitar que una clase que tiene ya se ha cargado se cargue de nuevo.

Por supuesto, Java desde el principio ha soportado la carga de clase dinámica, lo que es difícil es la carga de clase.

Se consideró dañino (y por una buena razón) que una aplicación Java en ejecución se inyectó con una nueva clase con código malicioso. Por ejemplo, una implementación craqueada de java.lang.String procedente de Internet, que en lugar de crear una cadena, elimina algún archivo aleatorio que invoque el método length().

Entonces, la manera en que se concibió Java (y supongo que .NET CLR en consecuencia, porque estaba muy "inspirado" en JVM) fue para evitar que una clase ya cargada cargue de nuevo esa misma máquina virtual.

Ofrecieron un mecanismo para anular esta "característica". Los cargadores de clases, pero una vez más las reglas para los cargadores de clase era que deberían pedir permiso al cargador de clases "padre" antes de intentar cargar una nueva clase, si el padre ya ha cargado la clase, se ignora la nueva clase.

Por ejemplo he utilizado cargadores de clases que se cargan a clases de LDAP o RDBMS

El despliegue en caliente se convierte en una necesidad en el mundo Java cuando el servidor de aplicaciones se convirtió en la corriente principal para Java EE (así como crear la necesidad de contenedores micro como la primavera para evitar este tipo de carga).

Reiniciar todo el servidor de aplicaciones después de cada compilación vuelve loco a cualquiera.

Por lo tanto, el proveedor de servidor de aplicaciones, ofrece estos cargadores de clases "personalizados" para ayudar a la implementación en caliente, y al usar un archivo de configuración, ese comportamiento DEBERÍA desactivarse cuando se establezca en producción. Pero la compensación es que tienes que usar toneladas de memoria en desarrollo. Entonces, la buena forma de hacerlo es reiniciar cada 3 o 4 implementaciones.

Esto no sucede con otros lenguajes que fueron diseñados desde el principio para cargar sus clases.

En Ruby, por ejemplo, incluso puede agregar métodos a una clase en ejecución, anular un método en tiempo de ejecución o incluso agregar un único método a un objeto específico único.

La compensación en este tipo de entornos es, por supuesto, la memoria y la velocidad.

Espero que esto ayude.

EDITAR

He encontrado este producto hace algún tiempo que promete que la recarga es hacer lo más simple posible. No recuerdo el enlace cuando escribí esta respuesta por primera vez, y lo hago.

Es JavaRebel from ZeroTurnaround

+0

qué desorden en tu cabeza, ¿eh? entonces java no fue diseñado para cargar clases en tiempo de ejecución, ¿eh? es por eso que tiene Class.forName() desde el principio? –

+0

Eso no es lo que Oscar escribió Vladimir. Escribió que Java no estaba diseñado para reemplazar clases en tiempo de ejecución. –

+0

Clase ClassLoader está allí desde la versión 1. ¿No está diseñado para reemplazar clases en tiempo de ejecución? Además, la seguridad no tiene nada que ver con PermGen OOM. El límite en PermGen es solo un desagradable detalle de implementación de Sun JVM. –

3

Sun JVM ha PermGen espacio fijo, y, finalmente, todo es consumido (sí, aparentemente debido a un error en el código relacionado con cargador de clases-) => OOM.

Si puede utilizar la JVM de otro proveedor (por ejemplo, Weblogic), amplía dinámicamente el espacio PermGen, por lo que nunca obtendrá OOM relacionado con permgen.

+0

Espera allí. Esto no es 100% cierto. El problema de la gema permanente no solo está relacionado con la JVM y sus bibliotecas. La aplicación incorrecta también puede causar errores PermGen. Además, la expansión dinámica de PerGen es absolutamente diferente de la solución. Espere suficiente tiempo en cualquier máquina virtual y el problema sucederá. – Antonio

+1

Correcto. Aún así, el PermGen dinámico le permite sobrevivir por más implementaciones en caliente. En Sun JVM a menudo ocurre con el redespliegue N. ° 1: -E –

0

¿Qué versión de java estás usando? Hubo errores en principios de Sun 1.4.2, pero ha estado funcionando durante mucho tiempo.
Por cierto, ¿cómo le darás la noticia al líder de tu equipo? ¿Eres el líder del equipo?

+0

Es en realidad un nuevo problema desde el 1.5. O al menos me encontré solo en 1.5. –

+0

Estamos teniendo este problema con 1.6. Probablemente "rompa esto" al líder de mi equipo enviándole un enlace a esta página :-P –

Cuestiones relacionadas