Posible pérdida de memoria en el número de clases cargadas en la aplicación Java

StackOverflow https://stackoverflow.com/questions/167740

  •  03-07-2019
  •  | 
  •  

Pregunta

Recientemente comencé a perfilar una aplicación osgi java que estoy escribiendo usando 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 del montón y el tamaño de PermGen permanecen constantes. El número de clases nunca cae, incluso después de que deja de enviar datos. ¿Es esta 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.

Para la captura de pantalla de mi aplicación de creación de perfiles, vaya a aquí

¿Fue útil?

Solución

  

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

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

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

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

He ejecutado mi programa nuevamente, y veo una meseta normal como es de esperar.

Otros consejos

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 liberar estas clases es liberar todas las referencias al cargador de clases.

Dado que cada clase contiene el cargador de clases, esto también significa que no debe liberar las referencias a todas las clases también [1]. Puede capturarlos con un generador de perfiles decente (uso Yourkit; busque varias instancias del cargador de clases con el mismo tamaño retenido)

Un problema es que la JVM no descarga las clases de forma predeterminada (el motivo es la compatibilidad con versiones anteriores, que la gente asume (erróneamente) que los inicializadores estáticos se ejecutarán solo una vez. La verdad es que se ejecutan cada vez que se carga una clase .) Para habilitar la descarga, debe pasar un poco de uso de 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, por lo que le sugiero que busque en su código para encontrar al culpable y almacenar en caché las clases generadas. Los delincuentes frecuentes son lenguajes de script, proxys dinámicos (incluidos los generados por los servidores de aplicaciones) o un gran modelo de Hibernate (en este caso, solo puede aumentar su permgen).

Ver 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

Es posible que encuentres algunas marcas de hotspot para comprender este comportamiento, como:

  • -XX: + TraceClassLoading
  • -XX: + TraceClassUnloading

Esta es una buena referencia:

http://java.sun.com/javase/technologies/hotspot /vmoptions.jsp

A menos que lo entienda mal, estamos viendo aquí las clases cargadas, no las instancias.

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

No estoy seguro bajo qué condiciones descargaría una clase. Ciertamente, nunca debe descargar ninguna clase con información estática.

Por lo tanto, esperaría un patrón más o menos como el suyo, donde a medida que su aplicación se ejecuta entra en áreas y hace referencia a nuevas clases, por lo que la cantidad de clases cargadas aumentaría y aumentaría.

Sin embargo, dos cosas me parecen extrañas:

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

Esperaría que tenga una tendencia ascendente, 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 línea de base. Luego, podría considerar la implementación de su propio ClassLoader que emita cierta información de depuración, o tal vez haya una herramienta para hacer que se informe.

Debe averiguar cuáles son las clases que se están cargando.

Sí, generalmente es una pérdida de memoria (dado que en realidad no tratamos directamente con la memoria, es más una pérdida de instancia de clase). He pasado por este proceso antes y, por lo general, 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 provoca el " escucha " objetar permanecer alrededor. Vería kits de herramientas más antiguos o que no hayan pasado por muchas revoluciones. Cualquier biblioteca existente desde hace mucho tiempo que se ejecute en un JDK posterior sabría acerca de los objetos de referencia que elimina el requisito de "Quitar escucha".

Además, llame a disponer en sus ventanas si las vuelve a crear cada vez. No creo que desaparezcan si tú no lo haces (en realidad también hay una disposición para cerrar la configuración).

No te preocupes por los oyentes de Swing o JDK, todos deberían usar referencias para que estés bien.

Use el Eclipse Memory Analyzer para verificar si hay clases duplicadas y pérdidas de memoria. Puede suceder que la misma clase se cargue más de una vez.

Saludos, Markus

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top