2010-10-18 12 views
14

Esta es una pila de memoria (sirve como caché) que consiste en nada más que una ConcurrentHashMap estática (CHM).Memoria Totalmente utilizada por Java ConcurrentHashMap (en Tomcat)

Todos los datos de solicitud HTTP entrantes se almacenan en este ConcurrentHashMap. Y hay un proceso de planificador asynch que toma los datos del mismo ConcurrentHashMap y elimina el valor clave después de almacenarlos en la base de datos.

Este sistema funciona muy bien y sin problemas, pero sólo descubre bajo criterios siguientes, la memoria se utiliza plenamente (2,5 GB) y se tomó todo el tiempo de CPU para realizar GC:

-concurrent http golpe de 1000/s

-mantener el mismo hit simultáneo durante un período de 15 minutos

El proceso de asincronización registra el tamaño restante del CHM cada vez que se escribe en la base de datos. El CHM.size() se mantiene alrededor de Min: 300 a Max: 3500

Pensé que hay una pérdida de memoria en esta aplicación. entonces usé Eclipse MAT para mirar el Heap Dump. Después de ejecutar el Informe del sospechoso, que me dieron estos comentarios de MAT:

Una instancia de "org.apache.catalina.session.StandardManager" cargados por "org.apache.catalina.loader.StandardClassLoader @ 0x853f0280" ocupa 2135429456 (94.76%) bytes. La memoria se acumula en una instancia de "java.util.concurrent.ConcurrentHashMap $ Segment []" cargado por "".

3,646,166 instances of java.util.concurrent.ConcurrentHashMap$Segment retain >= 2,135,429,456 bytes. 

y

Length # Objects  Shallow Heap  Retained Heap 
0   3,646,166  482,015,968  >= 2,135,429,456 

La longitud 0 por encima de i traduzco registro de longitud como vacío en el interior del CHM (cada vez CHM.remove() método i llamada). Es consistente con el número de registro dentro de la base de datos, 3,646,166 registros estaba dentro de la base de datos cuando este vertedero se creó

El escenario extraño es: si detengo la prueba de esfuerzo, la utilización de la memoria de pila liberará gradualmente a 25MB. Esto lleva entre 30 y 45 minutos. He vuelto a simular esta aplicación y las curvas es similar a la gráfica VisualVM a continuación: alt text

Heres las preguntas:

1) ¿Esto se parece a una pérdida de memoria?

2) Cada llamada de eliminación remove(Object key, Object value) para eliminar un <key:value> de CHM, ¿ese objeto eliminado obtiene GC?

3) ¿Esto tiene algo que ver con la configuración del GC? He agregado los siguientes parámetros de GC pero sin ayuda:

-XX:+UseParallelGC 

-XX:+UseParallelOldGC 

-XX:GCTimeRatio=19 

-XX:+PrintGCTimeStamps 

-XX:ParallelGCThreads=6 

-verbose:gc 

4) Cualquier idea para resolver esto es muy apreciada. :)

NUEVO 5) ¿Podría ser posible porque todas mis referencias son referencias difíciles? Según tengo entendido, mientras la sesión HTTP finalice, todas las variables que no sean estáticas están ahora disponibles para GC.

NUEVO Nota He intentado reemplazar el CHM con ehcache 2.2.0, pero me sale el mismo problema OutOfMemoryException. supongo que ehcache también está usando ConcurrentHashMap.

servidor Spec:

núcleo -Xeon Quad, 8 hilos.

memoria -4GB

-Windows 2008 R2

-Tomcat 6.0.29

+0

¿Cuán difícil sería reemplazar el mapa hash con una instancia de EhCache? Estas bibliotecas están optimizadas para este tipo de tareas. –

+0

Por el momento tratamos de no cambiar tanto al código existente porque aún no hemos analizado el impacto. EhCache fue inicialmente parte de la consideración, pero de alguna manera no se eligió como la opción de implementación. – Reusable

Respuesta

2

1) ¿Esto se parece a una pérdida de memoria?

Sí, si la aplicación sigue colocando objetos en el mapa y nunca los elimina, entonces podría ser una pérdida de memoria.

2) Cada llamada de quitar eliminar (Clave de objeto, Valor del objeto) para eliminar un CHM, ¿ese objeto eliminado obtiene GC?

Los objetos solo se pueden recoger como basura si no hay un subproceso en tiempo real (en ejecución) que tenga una referencia a ellos. El mapa es solo un lugar donde hay una referencia al objeto. Todavía podría haber otros lugares que tengan referencias al mismo objeto. Pero mantener el objeto en el mapa evitará que se recolecte basura.

3) ¿Esto tiene algo que ver con la configuración del GC?

No; si se hace referencia a un objeto, no puede ser basura recolectada; no importa cómo modifiques el recolector de basura.

+0

2da y 3 pregunta tengo que estar de acuerdo con usted completamente. Pero en cuanto a la primera pregunta, si se trata de una pérdida de memoria, ¿hay alguna posibilidad de que después de 30-45 minutos, la utilización de la memoria de pila Java vuelva a su estado de inicialización? – Reusable

+0

@Reusable si eso es lo que está ocurriendo, entonces no debería etiquetarse como una verdadera "pérdida de memoria". Sin embargo, si descubre que la memoria no se recupera después de que cree que debería ser (porque ya no está haciendo referencia a los datos), esto sugiere cierta lógica en su uso del Mapa. –

+0

@matt b estoy pensando en la misma línea que usted, pero es puramente mi suposición aproximada, que hay algún otro objeto que contiene la referencia de la clave/valor que se elimina. Sin éxito y aún revisando los códigos. – Reusable

10

¡Este problema me ha molestado durante los malos 7 días! ¡Y finalmente descubrí el verdadero problema! Debajo están las tareas en lo que he intentado pero no he podido resolver la Excepción de OutOfMemory:

-cambio de usar concurrenthashmap a ehcache. (Resulta ehcache también está utilizando ConcurrentHashMap)

-cambio toda la referencia difícil blando Referencia

-override la AbstractMap junto con concurrnetHashMap según sugiere por Dr. Heinz M. Kabutz

La pregunta del millón es realmente " ¿por qué 30-45 minutos más tarde, la memoria comienza a liberarse al grupo de montón? "

La causa real fue porque todavía hay algo más que contiene la sesión variable real, y el culpable es que la sesión http dentro de tomcat aún está activa. Por lo tanto, aunque la sesión http se completó, pero si la configuración del tiempo de espera es de 30 minutos, Tomcat retendrá la información de la sesión durante 30 minutos antes de que JVM pueda GC. El problema se resuelve inmediatamente después de cambiar la configuración de tiempo de espera a 1 minuto como prueba.

$tomcat_folder\conf\web.xml 

<session-config> 
    <session-timeout>1</session-timeout> 
</session-config> 

Espero que esto ayude a cualquier persona con problemas similares.

+0

Me pregunto si al cambiar al administrador de sesión persistente (usando el Almacén de archivos) resolvió su problema mientras mantiene el tiempo de espera de sesión? –

+0

Podría. solo tengo tiempo para volver a probar esto – Reusable

9

creo que está utilizando demasiadodatos de la sesión que no se ajuste a la vezen la memoria. Prueba con esto:

  1. Editar bin/setenv.sh o donde los argumentos de JVM se fijan en su lanzador Tomcat:

    Anexar -Dorg.apache.catalina.session.StandardSession.ACTIVITY_CHECK=true

    por ejemplo,

    # Default Java options 
    if [ -z "$JAVA_OPTS" ]; then 
         JAVA_OPTS="-server -Djava.awt.headless=true -XX:MaxPermSize=384m -Xmx1024m -Dorg.apache.catalina.session.StandardSession.ACTIVITY_CHECK=true" 
    fi 
    
  2. Editar conf/context.xml, antes </Context> añadir lo siguiente:

    <Manager className="org.apache.catalina.session.PersistentManager" 
         maxIdleBackup="60" maxIdleSwap="300"> 
        <Store className="org.apache.catalina.session.FileStore"/> 
    </Manager> 
    

Reiniciar Tomcat y su problema debe desaparecer, ya que va a tienda de tus sesiones utilizando el sistema de archivos lugar.

En mi configuración de session-timeout = 1 vista es una soluciónque enmascara la raíz del problema, y ​​es inservible en la mayoría de aplicaciones donde realmente se necesita suficiente session-timeout un grande. Nuestras aplicaciones (Bippo) generalmente tienen un session-timeout de 2880 minutos, es decir, 2 días.

Referencia: Tomcat 7.0 Session Manager Configuration

1

Por supuesto, es demasiado tarde para responder, pero sólo para otras personas que encontrarán esta pregunta de búsqueda. Puede ser útil.

Estos 2 enlaces son muy útiles
https://issues.apache.org/bugzilla/show_bug.cgi?id=50685
http://wiki.apache.org/tomcat/OutOfMemory

Brevemente, en la mayoría de los casos se trata de una prueba o pruebas de software mal. Cuando algún software personalizado abre URL, si este software no puede administrar sesión http, tomcat crea una nueva sesión para cada solicitud. Por ejemplo, es posible verificarlo con un código simple, que se puede agregar a JSP.

System.out.println("session id: " + session.getId()); 
System.out.println("session obj: " + session); 
System.out.println("session getCreationTime: " + (new Date(session.getCreationTime())).toString()); 
System.out.println("session.getValueNames().length: " + session.getValueNames().length); 

Si ID de sesión será el mismo para un usuario desde el punto de vista de la prueba de carga, que está muy bien, si cada solicitud genera nueva ID de sesión, eso significa que las pruebas de software no maneja las sesiones muy bien y resultado de la prueba no representa carga de usuarios reales.

Para algunas aplicaciones session.getValueNames(). Length también es importante, porque Por ejemplo, cuando el usuario normal funciona, permanece igual, pero cuando el software de prueba de carga hace lo mismo, crece. También significa que el software de prueba de carga no representa muy bien la carga de trabajo real. En mi caso session.getValueNames(). Length para el usuario normal era aproximadamente 100, pero qwith software de prueba de carga después de 10 minutos era aproximadamente 500 y finalmente el sistema falla con el mismo error OutOfMemory y MAT muestra lo mismo:

org. apache.catalina.loader.StandardClassLoader @ 0x853f0280 "ocupa 2,135,429,456 (94,76%) bytes.

0

Si obtiene esta excepción y utiliza la versión de arranque de resorte 1.4.4 RELEASE o inferior, establezca el valor de la propiedad "server.session-timeout" en minutos, en lugar de lo que sugieren (segundos), para que las sesiones el montón se limpiará a tiempo. O puede usar un bean de EmbeddedServletContainerCustomizer pero el valor proporcionado se establecerá en minutos.

ejemplo (sesión de tiempo de espera en 10 minutos): server.session-timeout = 10 (ajustado en el archivo de propiedades) container.setSessionTimeout (10, TimeUnit.SECONDS); (establecido en EmbeddedServletContainerCustomizer)

Cuestiones relacionadas