2011-06-14 14 views
15

Estoy viendo 'java.lang.OutOfMemoryError: PermGen space' mientras ejecuto ~ 300 pruebas de JUnit y usando Spring context. Tener un tiempo difícil averiguar lo que está comiendo PermGen desde:¿Qué, además de objetos de Clase, se almacena en Perm Gen Space (sol 1.6 VM)?

  • en estado estacionario, la aplicación consume alrededor de 90 millones de espacio PermGen
  • -XX He intentado: MaxPermSize = 256 m para las pruebas unitarias - sigue funcionando sin
  • Con -XX:+TraceClassLoading y -XX:+TraceClassUnloading activados, no veo eventos de "carga adicionales" mientras ejecuto las últimas 20-30 pruebas antes del OutOfMemoryError.

Este último parece indicar que algo además de los objetos de clase está llenando PermGen, ¿no? Si es así, ¿qué podría ser? Por ejemplo, ¿existen circunstancias cuando las instancias de clase se almacenan en PermGen?

Aquí está mi información VM:

$ java -version 
java version "1.6.0_25" 
Java(TM) SE Runtime Environment (build 1.6.0_25-b06) 
Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode) 

relacionados

Fwiw, la raíz de mi problema que precipitó este post resultó ser algo trivial: Supuse que Maven plugin de éxito seguro hereda la configuración de la máquina virtual de MAVEN_OPTS (o instancia de máquina virtual que ejecuta mvn) cuando bifurca una VM - no es (boo). Uno tiene que especificar los que usan explícitamente argLine en la configuración del complemento. HTH.

+1

JProfiler o YourKit le dará más información acerca de lo que hay allí. – bmargulies

+0

@bmergulies - ¡Excelente idea para usar un generador de perfiles! –

+0

Sin ver su código, mi primer pensamiento es que el contexto de primavera se está instanciando para cada caso de prueba. – Woot4Moo

Respuesta

4

A veces el abuso de String.intern() puede causar errores de espacio PermGen ya que las instancias de cadenas internas se almacenan en PermGen.

Esto podría ser lo que está viendo: intente eliminar llamadas String.intern() innecesarias para ver si esto resuelve el problema. En general, no recomendaría el uso de String.intern() a menos que esté seguro de que las dos condiciones siguientes son verdaderas:

  • Usted está seguro de que sólo se añadirá un número limitado de cadenas
  • realidad necesita este tipo de cadenas para compartir la misma identidad de objeto (por ejemplo, si muchos ejemplos de las mismas cadenas consumirían cantidades inaceptables de memoria o que necesitan depender de == para la comparación de cadenas complejas por razones de rendimiento)
+0

a la derecha, se olvidó de String.intern() - aceptando la respuesta (desafortunadamente, en mi caso esto NO fue la causa de OutOfMemoryError) – Nikita

+0

@Gweebz acepté la respuesta que abordaba más directamente la pregunta explícita que publiqué. Si eso es incorrecto, no quiero estar en lo cierto; - – Nikita

2

cuerdas cautiverio sean también almacenado en permgen, aunque cientos de megabytes de cadena parecen improbables. Recuerde que cada Spring Bean para el cual está usando proxies está generando nuevas clases sobre la marcha durante el tiempo de ejecución para implementar las interfaces que está proxysando. (O sus clases si está utilizando proxies CGLIB, etc.). Por lo tanto, si está creando un Spring ApplicationContext "nuevo" para cada JUnit, de hecho está produciendo 300 copias de todos sus proxies, etc.

También recuerde que las instancias de Class son únicas por classloader, no en todo el espacio permgen, por lo que puede haber duplicados dependiendo de cómo se configuren las ejecuciones (si implican la implementación en un contenedor o algo así, aunque eso también parece poco probable en un JUnit :)).

+0

Son buenos puntos, pero no estoy creando una nueva ctx Spring por prueba: inicialmente se crea una ctx, se comparte en todas las pruebas y se actualiza una vez durante la prueba 300 . Al generar nuevas clases sobre la marcha: sin embargo, se genera la clase, ¿no debería causar una entrada de registro [Cargando xxx] (debido a -XX: + TraceClassLoading) en el momento en que se genera? No los veo más allá de un tiempo – Nikita

1

Me di cuenta de que está ejecutando una JVM de 64 bits. Se sabe que estos usan el doble de memoria en su máquina porque requiere el doble de espacios de memoria por asignación.

Si tiene pruebas de JUnit que realmente cargan un contexto de primavera (no tan Unidad después de todo), estos instanciarán TODOS los beans en su appContext. Es muy probable que esto requiera más de 128 mb (256 mb en una caja de 64 bits) de memoria.

En mi experiencia, no es absurdo asignar medio giga o más a un conjunto de pruebas grande en una máquina de 64 bits. Intente subirlo a 512 mb o incluso 1 gb.

Estas son las opciones que corro una de banco de pruebas de mi proyecto más amplio con ...

-Xms256m 
-Xmx512m 
-XX:MaxPermSize=512m 
+0

Puede aliviar eso con CompressedOops si no necesita un gran montón: http://wikis.sun.com/display/HotSpotInternals/CompressedOops – gpeche

Cuestiones relacionadas