java - ¿Qué hace que la implementación en caliente sea un "problema difícil"?
jvm hotdeploy (4)
¿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 vas a darle la noticia al líder de tu equipo? ¿Eres el líder del equipo?
En el trabajo, hemos tenido un problema con las excepciones de " PermGen sin memoria ", y el líder del equipo decidió que se trataba de un error en la JVM, algo relacionado con la implementación de código en caliente. 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.
Encontré muchos artículos que explican el despliegue en caliente a vista de 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"?
Sun JVM tiene el espacio PermGen corregido, y finalmente todo se consume (sí, aparentemente debido a un error en el código relacionado con el cargador de clases) => OOM.
Si puede usar la JVM de otro proveedor (p. Ej., Weblogic), amplía dinámicamente el espacio PermGen, por lo que nunca obtendrá OOM relacionado con permgen.
El problema en términos generales es el modelo de seguridad de Java que realmente intenta evitar que una clase que 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 forma en que se concibió Java (y presumo que .NET CLR en consecuencia, porque estaba muy "inspirado" en JVM) fue para evitar que una clase ya cargada volviera a cargar 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 usado clasificadores que cargan una clase desde LDAP o RDBMS
El despliegue en caliente se convierte en una necesidad en el mundo de Java cuando el servidor de aplicaciones se convirtió en la corriente principal para Java EE (y también crea la necesidad de micro contenedores 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.
Así que el proveedor del 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 estar deshabilitado cuando se configura 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, puede incluso 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 un tiempo que promete que la recarga es lo más simple posible. No recuerdo el enlace cuando escribí esta respuesta por primera vez, y lo hago.
Cuando se carga una clase, varios datos estáticos sobre la clase se almacenan 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 en caliente suficientes veces, eventualmente agotará su espacio PermGen.
Si su aplicación web no se cierra por completo cuando no se implementa, por ejemplo, si deja un Thread ejecutándose, todas las instancias de Class utilizadas por esa aplicación web se fijarán en el espacio de 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 puedes arreglar esto por:
- Suministrar argumentos de comando a una Sun JVM reciente para habilitar GC en PermGen y de clases. Es decir:
-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
- Usar una JVM diferente que no emplea un PermGen de tamaño fijo o que tiene GC en clases cargadas
Pero esto solo será útil si su aplicación web se apaga de manera completa y limpia, sin dejar referencias en vivo a ninguna de las instancias de clase de ninguna clase que carguen 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 buenos diagramas para ilustrar parte del problema).