2008-10-03 12 views
9

Recientemente comencé a perfilar una aplicación osgi java que estoy escribiendo utilizando VisualVM. Una cosa que he notado es que cuando la aplicación comienza a enviar datos a un cliente (a través de JMS), el número de clases cargadas comienza a aumentar a un ritmo constante. Sin embargo, el tamaño de Heap y el tamaño de PermGen permanecen constantes. El número de clases nunca baja, incluso después de que deja de enviar datos. ¿Es esto una pérdida de memoria? Creo que sí, porque las clases cargadas deben almacenarse en algún lugar, sin embargo, el montón y el permgen nunca aumentan incluso después de ejecutar la aplicación durante varias horas.Posible pérdida de memoria en el número de clases cargadas en la aplicación Java

Para la captura de pantalla de mi solicitud de perfiles ir here

Respuesta

5

¿Estás creando dinámicamente nuevas clases sobre la marcha de alguna manera?

Gracias por su ayuda. Descubrí cuál es el problema. En una de mis clases, estaba usando Jaxb para crear una cadena XML. Al hacer esto, JAXB busca la reflexión para crear una nueva clase.

JAXBContext context = JAXBContext.newInstance(this.getClass()); 

Así que aunque el JAXBContext no decía nada en el montón, las clases se habían cargado.

He vuelto a ejecutar mi programa y veo una meseta normal como era de esperar.

+6

¿Puedo preguntarle cómo se deshizo de la creación de la nueva clase? -Estoy luchando contra este problema y me gustaría probar tu solución. -Gracias – Ytsejammer

0

sí, es por lo general una pérdida de memoria (ya que no se sabe muy ocupamos de memoria directamente, es más de una fuga instancia de clase). He pasado por este proceso antes y generalmente es un oyente agregado a un kit de herramientas antiguo que no se eliminó por sí mismo.

En el código anterior, una relación de escucha hace que el objeto "oyente" permanezca alrededor. Vería kits de herramientas más antiguos o que no han tenido muchas revoluciones. Cualquier biblioteca antigua existente que se ejecute en un JDK posterior sabrá sobre los objetos de referencia, lo que elimina el requisito de "Eliminar el oyente".

Además, llame a deshacerse de sus ventanas si las vuelve a crear cada vez. No creo que se vayan nunca si no lo haces (en realidad también hay una disposición en entornos cerrados).

No se preocupe por los oyentes Swing o JDK, todos deberían usar referencias, así que debería estar bien.

3

A menos que malinterprete, estamos viendo clases cargadas, no instancias.

Cuando su código hace referencia por primera vez a una clase, la JVM hace que el cargador de clases salga y obtenga la información sobre la clase de un archivo .class o similar.

No estoy seguro de en qué condiciones podría descargar una clase. Ciertamente, nunca debería descargar ninguna clase con información estática.

Así que esperaría un patrón más o menos como el suyo, mientras que cuando la aplicación se ejecuta entra en áreas y hace referencia a nuevas clases, por lo que el número de clases cargadas subiría o aumentaría.

Sin embargo, hay dos cosas que me extraña:

  1. Por qué es tan lineal?
  2. ¿Por qué no es meseta?

Esperaría que tuviera una tendencia al alza, pero en una línea tambaleante, y luego disminuya en el aumento ya que la JVM ya ha cargado la mayoría de las clases a las que hace referencia su programa. Quiero decir, hay un número finito de clases referenciadas en la mayoría de las aplicaciones.

¿Estás creando dinámicamente nuevas clases sobre la marcha de alguna manera?

Sugeriría ejecutar una aplicación de prueba más simple a través del mismo depurador para obtener un caso de referencia. Luego, podría considerar implementar su propio ClassLoader que contenga alguna información de depuración, o tal vez haya una herramienta para informarlo.

Necesita averiguar cuáles son estas clases que se están cargando.

4

Estoy dispuesto a apostar que su problema está relacionado con la generación de bytecode.

Muchas bibliotecas usan CGLib, BCEL, Javasist o Janino para generar bytecode para nuevas clases en tiempo de ejecución y luego cargarlas desde el cargador de clases controlado. La única forma de lanzar estas clases es lanzar todas las referencias al cargador de clases.

Dado que el cargador de clases se mantiene en cada clase, esto también significa que no debe liberar las referencias a todas las clases también [1]. Puede capturar estos con un generador de perfiles decente (Yo uso Yourkit - buscar múltiples instancias del cargador de clases con el mismo tamaño retenido)

Una pega es que la JVM no descarga las clases por defecto (la razón es la compatibilidad hacia atrás - que la gente asume . (erróneamente) que inicializadores estáticos se ejecutan sólo una vez La verdad es que se ejecutan cada vez que se carga una clase) Para habilitar la descarga, debe pasar algunos utilizan las siguientes opciones:.

-XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled 

(probado con JDK 1.5)

Incluso entonces, la generación excesiva de bytecode no es una buena idea, entonces su Cuanto más busca en su código para encontrar el culpable y almacenar en caché las clases generadas. Los delincuentes frecuentes son lenguajes de scripting, proxies dinámicos (incluidos los generados por los servidores de aplicaciones) o el enorme modelo de Hibernate (en este caso, puede simplemente aumentar su permgen).

Consulte también:

  1. http://blogs.oracle.com/watt/resource/jvm-options-list.html
  2. http://blogs.oracle.com/jonthecollector/entry/presenting_the_permanent_generation
  3. http://forums.sun.com/thread.jspa?messageID=2833028
+0

Incluso la JVM generará clases para java.lang.reflect.Proxy y Warped Field.get/set, Method.invoke y Constructor.newInstance (IIRC). –

+0

Sí, tienes razón, aún estas clases generadas se almacenan en caché en la capa de reflexión, por lo que no debería ser motivo de fugas de permgen. – ddimitrov

0

Uso del Eclipse Memory Analyzer para comprobar si hay clases duplicados y pérdidas de memoria. Puede suceder que la misma clase se cargue más de una vez.

Saludos, Markus

Cuestiones relacionadas