servlet servidor programar mundo hola ejemplo java performance tomcat7

java - servidor - ¿Qué podría causar la desaceleración global de Tomcat/JVM?



servidor tomcat (7)

¿Alguien tiene una idea de dónde mirar?

  1. El problema podría estar en Tomcat / JVM: ¿tiene algún trabajo por lotes que se active y acentúe los recursos compartidos como una base de datos común?

  2. Haga un volcado de subprocesos y vea qué están haciendo los procesos de Java cuando explota el tiempo de respuesta de la aplicación?

  3. Si está utilizando Linux, use una herramienta como Strace y compruebe qué está haciendo el proceso de Java.

Tengo un problema extraño pero grave al ejecutar varias (aproximadamente 15) instancias de aplicaciones web Java EE-ish (Hibernate 4 + Spring + Quartz + JSF + Facelets + Richfaces) en Tomcat 7 / Java 7.

El sistema funciona bien, pero después de una gran cantidad de tiempo, todas las instancias de la aplicación al mismo tiempo sufren repentinamente tiempos de respuesta en aumento. Básicamente, la aplicación aún funciona, pero los tiempos de respuesta son aproximadamente tres veces mayores.

Estos son dos diagramas que muestran el tiempo de respuesta de dos ciertos flujos de trabajo / acciones cortos (iniciar sesión, acceder a la lista de seminarios, ajax-actualizar esta lista, cerrar sesión; la línea inferior es solo el tiempo de solicitud para la actualización ajax) de dos ejemplos de instancias de la aplicación:

Como puede ver, ambas instancias de la aplicación "explotan" al mismo tiempo y permanecen lentas. Después de reiniciar el servidor, todo vuelve a la normalidad. Todas las instancias de la aplicación "explotan" simultáneamente.

Estamos almacenando los datos de la sesión en una base de datos y usamos esto para clustering. Verificamos el tamaño y el número de la sesión y ambos son bastante bajos (lo que significa que en otros servidores con otras aplicaciones a veces tenemos sesiones más grandes y más). El otro Tomcat en el clúster generalmente se mantiene rápido durante algunas horas más y después de esta cantidad aleatoria de tiempo también "muere". Comprobamos los tamaños de almacenamiento dinámico con jconsole y el almacenamiento dinámico principal se mantiene entre 2,5 y 1 GB de tamaño, el grupo de conexiones de db está básicamente lleno de conexiones gratuitas, así como de los grupos de subprocesos. El tamaño de almacenamiento dinámico máximo es de 5 GB, también hay mucho espacio de memoria permanente disponible. La carga no es especialmente alta; hay solo un 5% de carga en la CPU principal. El servidor no intercambia. Tampoco es un problema de hardware ya que, además, implementamos las aplicaciones en una VM donde los problemas siguen siendo los mismos.

Ya no sé dónde buscar, me he quedado sin ideas. ¿Alguien tiene una idea de dónde mirar?

2013-02-21 Actualización: ¡Nuevos datos!

Agregué dos rastros de temporización más a la aplicación. En cuanto a la medición: el sistema de monitoreo llama a un servlet que realiza dos tareas, mide el tiempo de ejecución para cada una en el servidor y escribe el tiempo tomado como respuesta. Estos valores son registrados por el sistema de monitoreo.

Tengo varios datos nuevos e interesantes: una nueva implementación de la aplicación hace que esta única instancia en el Tomcat actual se vuelva loca. Esto también parece afectar el rendimiento del cálculo crudo de la CPU (ver a continuación). Esta explosión de contexto individual es diferente de la explosión de contexto general que ocurre al azar.

Ahora para algunos datos:

Primero las líneas individuales:

  1. Azul claro es el tiempo total de ejecución de un flujo de trabajo pequeño (detalles ver arriba), medido en el cliente
  2. El rojo es "parte" de azul claro y es el tiempo necesario para realizar un paso especial de ese flujo de trabajo, medido en el cliente
  3. El azul oscuro se mide en la aplicación y consiste en leer una lista de entidades del DB a través de Hibernate e iterar sobre esa lista, obteniendo colecciones perezosas y entidades perezosas.
  4. Green es un pequeño benchmark de CPU que usa operaciones de punto flotante y entero. Por lo que veo no hay asignación de objetos, por lo que no hay basura.

Ahora para las etapas individuales de la explosión: marqué cada imagen con tres puntos negros. La primera es una "pequeña" explosión en más o menos una sola instancia de aplicación: en Inst1 salta (especialmente visible en la línea roja), mientras que Inst2 más abajo permanece más o menos en calma.

Después de esta pequeña explosión, se produce el "Big Bang" y todas las instancias de aplicación en ese Tomcat explotan (segundo punto). Tenga en cuenta que esta explosión afecta a todas las operaciones de alto nivel (procesamiento de solicitud, acceso a base de datos), pero no a la referencia de CPU. Se mantiene bajo en ambos sistemas.

Después de eso, reinvirtí Hot-Inst1 tocando el archivo context.xml. Como dije antes, esta instancia va de explotada a completamente devestada ahora (la línea azul clara está fuera del gráfico, es alrededor de 18 segundos). Tenga en cuenta cómo a) este redespliegue no afecta a Inst2 en absoluto yb) cómo el acceso de base de datos sin procesar de Inst1 tampoco se ve afectado, ¡pero cómo la CPU de repente parece haberse vuelto más lenta! . Esto es una locura, digo.

Actualización de la actualización El oyente de prevención de fugas de Tomcat no lloriquea acerca de ThreadLocals o Threads cuando la aplicación no se ha desplegado. Obviamente, parece haber algún problema de limpieza (que supongo que no está directamente relacionado con el Big Bang), pero Tomcat no tiene una pista para mí.

2013-02-25 Actualización: entorno de aplicaciones y calendario de cuarzo

El entorno de la aplicación no es muy sofisticado. Aparte de los componentes de red (no sé lo suficiente sobre ellos), hay básicamente un servidor de aplicaciones (Linux) y dos servidores de bases de datos (MySQL 5 y MSSQL 2008). La carga principal está en el servidor MSSQL, la otra simplemente sirve como un lugar para almacenar las sesiones.

El servidor de aplicaciones ejecuta un Apache como equilibrador de carga entre dos Tomcats. Entonces tenemos dos JVMs ejecutándose en el mismo hardware (dos instancias de Tomcat). Utilizamos esta configuración para no equilibrar realmente la carga, ya que el servidor de aplicaciones es capaz de ejecutar la aplicación muy bien (lo que hizo durante años) pero para permitir pequeñas actualizaciones de aplicaciones sin tiempo de inactividad. La aplicación web en cuestión se implementa como contextos separados para diferentes clientes, alrededor de 15 contextos por Tomcat. (Parece que mezclé "instancias" y "contextos" en mi publicación; aquí, en la oficina, a menudo se utilizan como sinónimos y generalmente sabemos mágicamente de lo que habla el colega. Malo, lo siento mucho).

Para aclarar la situación con una mejor redacción : los diagramas que publiqué muestran los tiempos de respuesta de dos contextos diferentes de la misma aplicación en la misma JVM. El Big Bang afecta a todos los contextos en una JVM, pero no ocurre en la otra (el orden en que los Tomcats explotan es aleatorio por cierto). Después del redistribución en caliente, un contexto en una instancia de Tomcat se vuelve loco (con todos los divertidos efectos secundarios, como una CPU aparentemente más lenta para ese contexto).

La carga general en el sistema es bastante baja. Es un software interno interno relacionado con el negocio con aproximadamente 30 usuarios activos simultáneamente. Las solicitudes específicas de aplicaciones (toques de servidor) se encuentran actualmente en alrededor de 130 por minuto. El número de solicitudes individuales es bajo, pero las solicitudes en sí mismas a menudo requieren varios cientos de selecciones para la base de datos, por lo que son bastante costosas. Pero generalmente todo es perfectamente aceptable. La aplicación tampoco crea cachés infinitos grandes; algunos datos de búsqueda se almacenan en caché, pero solo durante un corto período de tiempo.

Arriba escribí que los servidores eran capaces de ejecutar la aplicación sin problemas durante varios años. Sé que la mejor manera de encontrar el problema sería averiguar exactamente cuándo las cosas salieron mal por primera vez y ver qué se ha cambiado en este marco temporal (en la propia aplicación, las bibliotecas o la infraestructura asociadas), sin embargo, el problema es que no sabemos cuándo ocurrió el problema por primera vez. Simplemente llamemos a esa monitorización de aplicaciones subóptima (en el sentido de ausente) ...: - /

Hemos descartado algunos aspectos, pero la aplicación se ha actualizado varias veces durante los últimos meses y, por lo tanto, no podemos simplemente implementar una versión anterior. La actualización más grande que no fue un cambio de funciones fue un cambio de JSP a Facelets. Pero aún así, "algo" debe ser la causa de todos los problemas, sin embargo, no tengo idea de por qué Facelets, por ejemplo, debería influir en los tiempos de consulta de bases de datos.

Cuarzo

En cuanto al horario de Quartz: hay un total de 8 trabajos. La mayoría de ellos se ejecutan solo una vez al día y tienen que ver con la sincronización de datos de gran volumen (absolutamente no "grande" como en "big data large"; es más de lo que el usuario promedio ve a través de su trabajo diario habitual). Sin embargo, esos trabajos funcionan durante la noche y los problemas ocurren durante el día. Omito una lista detallada de trabajos aquí (si es beneficioso puedo proporcionar más detalles por supuesto). El código fuente de los trabajos no se ha modificado durante los últimos meses. Ya he comprobado si las explosiones se alinean con los trabajos, pero los resultados no son concluyentes en el mejor de los casos. De hecho, diría que no se alinean, pero como hay varios trabajos que se ejecutan cada minuto, no puedo descartarlo por el momento. Los trabajos acutales que se ejecutan cada minuto son bastante bajos en mi opinión, generalmente verifican si hay datos disponibles (en diferentes fuentes, DB, sistemas externos, cuenta de correo electrónico) y si es así, escríbelos en el DB o preséntelos a otro sistema .

Sin embargo, actualmente estoy habilitando el registro de ejecución de trabajos individuales para poder ver exactamente la marca de tiempo de inicio y finalización de cada ejecución de trabajo individual. Tal vez esto proporciona más información.

Actualización de 2013-02-28: Fases y tiempo de JSF

Agregué manualmente un oyente JSF phae a la aplicación. Ejecuté una llamada de muestra (la actualización ajax) y esto es lo que tengo (izquierda: instancia de Tomcat en ejecución normal, derecha: instancia de Tomcat después de Big Bang; los números se han tomado casi simultáneamente de ambos Tomcats y están en milisegundos):

  1. RESTORE_VIEW: 17 vs 46
  2. APPLY_REQUEST_VALUES: 170 vs 486
  3. VALIDACIONES_ PROCESO: 78 vs 321
  4. UPDATE_MODEL_VALUES: 75 vs 307
  5. RENDER_RESPONSE: 1059 vs 4162

La actualización de Ajax en sí pertenece a un formulario de búsqueda y su resultado de búsqueda. También hay otro retraso entre el filtro de solicitud más externo de la aplicación y el flujo web comienza su trabajo: hay un FlowExecutionListenerAdapter que mide el tiempo empleado en ciertas fases del flujo de la web. Este oyente informa 1405 ms para "Solicitud enviada" (que es, hasta donde yo sé, el primer evento de flujo web) de un total de 1632 ms para la solicitud completa en un Tomcat no explotado, por lo que calculo unos 200 ms de sobrecarga.
Pero en el Tomcat explotado informa 5332 ms para la solicitud enviada (lo que significa que todas las fases JSF ocurren en esos 5 segundos) de una duración total de solicitud de 7105 ms, por lo tanto, estamos a casi 2 segundos de sobrecarga para todo lo que no se haya enviado. .
Debajo de mi filtro de medición, la cadena del filtro contiene una org.ajax4jsf.webapp.BaseFilter , luego se llama al servlet Spring.

Actualización de 2013-06-05: Todo lo que sucede en las últimas semanas

Una actualización pequeña y bastante tardía ... el rendimiento de la aplicación todavía es una mierda después de un tiempo y el comportamiento sigue siendo errático. La creación de perfiles aún no ayudaba mucho, solo generaba una enorme cantidad de datos difíciles de analizar. (Intente hurgar en los datos de rendimiento o perfilar un sistema de producción ... suspiro) Llevamos a cabo varias pruebas (eliminando ciertas partes del software, eliminando otras aplicaciones, etc.) y en realidad tuvimos algunas mejoras que afectan a toda la aplicación. El modo de descarga predeterminado de nuestro EntityManager es AUTO y durante la visualización, se emiten muchos registros y selecciones, siempre incluyendo la verificación de si es necesario enjuagar.
Así que construimos un detector de fase JSF que establece el modo de descarga en COMMIT durante RENDER_RESPONSE . Esto mejoró mucho el rendimiento general y parece haber mitigado un poco los problemas.

Sin embargo, nuestra monitorización de aplicaciones sigue produciendo resultados y resultados completamente dementes en algunos contextos en algunas instancias de tomcat. Como una acción que debe terminar en menos de un segundo (y que realmente lo hace después de la implementación) y que ahora lleva más de cuatro segundos. (Estos números son compatibles con el tiempo manual en los navegadores, por lo que no es la supervisión la que causa los problemas).

Vea la siguiente imagen, por ejemplo:

Este diagrama muestra dos instancias de tomcat ejecutando el mismo contexto (es decir, mismo db, misma configuración, mismo contenedor). De nuevo, la línea azul es la cantidad de tiempo que tardan las operaciones de lectura de base de datos pura (obtener una lista de entidades, iterar sobre ellas, recopilar colecciones de forma perezosa y datos asociados). La línea turquesa y roja se miden al hacer varias vistas y hacer una actualización de Ajax, respectivamente. Los datos presentados por dos de las solicitudes en turquesa-ish y rojo son en su mayoría los mismos que se solicitan para la línea azul.

Ahora alrededor de 0700 en la instancia 1 (derecha) hay un gran aumento en el tiempo puro de DB que también parece afectar los tiempos reales de respuesta del renderizado, pero solo en Tomcat 1. Tomcat 0 no se ve afectado por esto, por lo que no puede ser causado por el DB servidor o red con ambos tomcat corriendo en el mismo hardware físico. Tiene que ser un problema de software en el dominio de Java.

Durante mis últimas pruebas descubrí algo interesante: todas las respuestas contienen el encabezado "X-Powered-By: JSF / 1.2, JSF / 1.2". Algunas (las respuestas de redirección producidas por WebFlow) incluso tienen "JSF / 1.2" tres veces allí.
Recorrí las partes del código que establecen esos encabezados y la primera vez que se establece este encabezado es causado por esta pila:

... at org.ajax4jsf.webapp.FilterServletResponseWrapper.addHeader(FilterServletResponseWrapper.java:384) at com.sun.faces.context.ExternalContextImpl.<init>(ExternalContextImpl.java:131) at com.sun.faces.context.FacesContextFactoryImpl.getFacesContext(FacesContextFactoryImpl.java:108) at org.springframework.faces.webflow.FlowFacesContext.newInstance(FlowFacesContext.java:81) at org.springframework.faces.webflow.FlowFacesContextLifecycleListener.requestSubmitted(FlowFacesContextLifecycleListener.java:37) at org.springframework.webflow.engine.impl.FlowExecutionListeners.fireRequestSubmitted(FlowExecutionListeners.java:89) at org.springframework.webflow.engine.impl.FlowExecutionImpl.resume(FlowExecutionImpl.java:255) at org.springframework.webflow.executor.FlowExecutorImpl.resumeExecution(FlowExecutorImpl.java:169) at org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:183) at org.springframework.webflow.mvc.servlet.FlowController.handleRequest(FlowController.java:174) at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:48) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:920) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:827) at javax.servlet.http.HttpServlet.service(HttpServlet.java:641) ... several thousands ;) more

La segunda vez que se establece este encabezado

at org.ajax4jsf.webapp.FilterServletResponseWrapper.addHeader(FilterServletResponseWrapper.java:384) at com.sun.faces.context.ExternalContextImpl.<init>(ExternalContextImpl.java:131) at com.sun.faces.context.FacesContextFactoryImpl.getFacesContext(FacesContextFactoryImpl.java:108) at org.springframework.faces.webflow.FacesContextHelper.getFacesContext(FacesContextHelper.java:46) at org.springframework.faces.richfaces.RichFacesAjaxHandler.isAjaxRequestInternal(RichFacesAjaxHandler.java:55) at org.springframework.js.ajax.AbstractAjaxHandler.isAjaxRequest(AbstractAjaxHandler.java:19) at org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.createServletExternalContext(FlowHandlerAdapter.java:216) at org.springframework.webflow.mvc.servlet.FlowHandlerAdapter.handle(FlowHandlerAdapter.java:182) at org.springframework.webflow.mvc.servlet.FlowController.handleRequest(FlowController.java:174) at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:48) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:920) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:827) at javax.servlet.http.HttpServlet.service(HttpServlet.java:641)

No tengo idea de si esto podría indicar un problema, pero no me di cuenta de esto con otras aplicaciones que se ejecutan en cualquiera de nuestros servidores, por lo que esto podría proporcionar algunas pistas. Realmente no tengo idea de lo que está haciendo ese código de marco (sin duda, todavía no me metí en él) ... tal vez alguien tiene una idea? ¿O estoy corriendo hacia un callejón sin salida?

Apéndice

El código de punto de referencia de mi CPU consiste en un ciclo que calcula Math.tan y usa el valor del resultado para modificar algunos campos en la instancia del servlet (no es volátil / sincronizado allí), y en segundo lugar realiza varios cálculos crudos enteros. Esto no es estrictamente sofisticado, lo sé, pero bueno ... parece mostrar algo en los gráficos, sin embargo, no estoy seguro de lo que muestra. Realizo las actualizaciones de campo para evitar que HotSpot optimice todo mi valioso código;)

long time2 = System.nanoTime(); for (int i = 0; i < 5000000; i++) { double tan = Math.tan(i); if (tan < 0) { this.l1++; } else { this.l2++; } } for (int i = 1; i < 7500; i++) { int n = i; while (n != 1) { this.steps++; if (n % 2 == 0) { n /= 2; } else { n = n * 3 + 1; } } } // This execution time is written to the client. time2 = System.nanoTime() - time2;


Solución

Aumente el tamaño máximo de la memoria caché de código:

-XX:ReservedCodeCacheSize=256m

Fondo

Estamos utilizando ColdFusion 10, que se ejecuta en Tomcat 7 y Java 1.7.0_15. Nuestros síntomas fueron similares a los tuyos. Ocasionalmente, los tiempos de respuesta y el uso de la CPU en el servidor aumentarían mucho sin motivo aparente. Parecía como si la CPU fuera más lenta. La única solución fue reiniciar ColdFusion (y Tomcat).

Analisis inicial

Empecé mirando el uso de la memoria y el registro del recolector de basura. No había nada allí que pudiera explicar nuestros problemas.

El siguiente paso fue programar un volcado de almacenamiento dinámico cada hora y realizar muestreos regularmente con VisualVM. El objetivo era obtener datos de antes y después de una desaceleración para poder compararlos. Logré lograr eso.

Hubo una función en el muestreo que se destacó: get () en coldfusion.runtime.ConcurrentReferenceHashMap. Se pasó mucho tiempo después de la desaceleración en comparación con muy poco antes. Pasé algún tiempo comprendiendo cómo funcionaba la función y desarrollé la teoría de que tal vez había un problema con la función hash que daba como resultado algunos cubos enormes. Usando los volcados del montón pude ver que los cubos más grandes solo contenían 6 elementos, así que descarté esa teoría.

Caché de código

Finalmente, entré en el camino correcto cuando leí "Java Performance: The Definitive Guide". Tiene un capítulo sobre el compilador JIT que habla de la memoria caché de código que no había escuchado antes.

Compilador deshabilitado

Al monitorear el número de compilaciones realizadas (monitoreadas con jstat) y el tamaño del caché de código (monitoreado con el plugin Memory Pools de VisualVM) vi que el tamaño aumentaba hasta el tamaño máximo (que es 48 MB por defecto en nuestro entorno - - el valor predeterminado varía según la versión de Java y el compilador de Java). Cuando la memoria caché de código se llenó, el compilador JIT se apagó. He leído que "CodeCache está lleno. El compilador se ha desactivado". debería imprimirse cuando eso ocurra, pero no vi ese mensaje; tal vez la versión que estamos usando no tenga ese mensaje. Sé que el compilador se apagó porque el número de compilaciones dejó de aumentar.

La desoptimización continúa

El compilador JIT puede desoptimizar las funciones previamente compiladas, lo que hará que la función sea ejecutada nuevamente por el intérprete (a menos que la función sea reemplazada por una compilación mejorada). La función desoptimizada puede ser recogida de basura para liberar espacio en la caché de código.

Por alguna razón, las funciones continuaron siendo desoptimizadas aunque no se compiló nada para reemplazarlas. Más y más memoria estaría disponible en Code Cache, pero el compilador JIT no se reinició.

Nunca tuve -XX: + PrintCompilation habilitado cuando experimentamos una desaceleración, pero estoy bastante seguro de que habría visto que ConcurrentReferenceHashMap.get (), o una función de la que depende, se desoptimizaría en ese momento.

Resultado

No hemos visto ninguna desaceleración desde que aumentamos el tamaño máximo de la memoria caché de código a 256 MB y también hemos visto una mejora general en el rendimiento. Actualmente hay 110 MB en nuestro Caché de código.


¿Has comprobado los tiempos de JVM GC? Algunos algoritmos GC pueden ''detener'' los hilos de la aplicación y aumentar el tiempo de respuesta.

Puede usar la utilidad jstat para controlar las estadísticas de recolección de basura:

jstat -gcutil <pid of tomcat> 1000 100

El comando anterior imprimiría las estadísticas de GC cada 1 segundo por 100 veces. Mire las columnas FGC / YGC, si el número sigue aumentando, hay algo mal con sus opciones de GC.

Es posible que desee cambiar a CMS GC si desea mantener el tiempo de respuesta bajo:

-XX:+UseConcMarkSweepGC

Puede consultar más opciones de GC aquí .


¿Qué sucede después de que tu aplicación se está ejecutando lento por un tiempo, vuelve a funcionar bien? De ser así, verificaría si hay alguna actividad que no esté relacionada con su aplicación que esté teniendo lugar en este momento. Algo así como un análisis antivirus o una copia de seguridad del sistema / db.

Si no, sugeriría que se ejecute con un generador de perfiles (JProfiler, yourkit, etc.) estas herramientas pueden indicarle puntos de acceso muy fácilmente.


En primer lugar, déjeme decir que ha hecho un excelente trabajo al obtener datos detallados sobre el problema; Me gusta mucho cómo dejas en claro lo que sabes y lo que especula: realmente ayuda.

EDITAR 1 edición masiva después de la actualización en contexto vs. instancia

Podemos descartar:

  • GC (eso afectaría el hilo del servicio de referencia de la CPU y aumentaría la CPU principal)
  • Trabajos de cuarzo (que afectarían tanto a Tomcats como a la referencia de CPU)
  • La base de datos (que afectaría a ambos Tomcats)
  • Tormentas de paquetes de red y similares (que afectarían a ambos Tomcats)

Creo que estás sufriendo un aumento en la latencia en algún lugar de tu JVM. Latencia es donde un subproceso está esperando (sincrónicamente) una respuesta de alguna parte: ha aumentado el tiempo de respuesta de su servlet pero sin costo para la CPU. Las latencias típicas son causadas por:

  • Llamadas de red, incluido
    • JDBC
    • EJB o RMI
    • JNDI
    • DNS
    • Archivos compartidos
  • Lectura y escritura en disco
  • Enhebrado
    • Lectura desde (y algunas veces escribiendo) colas
    • método synchronized o bloque
    • futures
    • Thread.join()
    • Object.wait()
    • Thread.sleep()

Confirmando que el problema es latencia

Sugiero usar una herramienta de perfil comercial. Me gusta [JProfiler] ( http://www.ej-technologies.com/products/jprofiler/overview.html , la versión de prueba de 15 días está disponible) pero YourKit también es recomendado por la comunidad de . En esta discusión utilizaré la terminología JProfiler.

Adjúntese al proceso de Tomcat mientras está funcionando bien y tenga una idea de cómo se ve en condiciones normales. En particular, utilice las sondas JDBC, JPA, JNDI, JMS, servlet, socket y de archivo de alto nivel para ver cuánto tardan las operaciones JDBC, JMS, etc. ( screencast . Ejecute esto de nuevo cuando el servidor muestre problemas y compare. verá lo que se ha ralentizado con precisión. En la captura de pantalla del producto a continuación, puede ver los tiempos SQL usando la sonda JPA:

Hotspots JPA http://static-aws.ej-technologies.com/SanJPN2pU9HB3g30N03BZsAwd77YzUtpXAsZoe9VUCi.png

Sin embargo, es posible que las sondas no hayan aislado el problema, por ejemplo, podría tratarse de un problema de subprocesamiento. Vaya a la vista de subprocesos para la aplicación; esto muestra un gráfico en ejecución de los estados de cada hilo, y si se está ejecutando en la CPU, en un Object.wait() , está esperando ingresar a un bloque synchronized o está esperando en E / S de red. Cuando sepa qué subproceso o subprocesos exhibe el problema, vaya a las vistas de CPU, seleccione el subproceso y use el selector de estados de subproceso para explorar inmediatamente los costosos métodos y sus llamadas. [Screencast] (( screencast ). Podrá profundizar en el código de su aplicación.

Esta es una pila de llamadas para el tiempo ejecutable:

Y este es el mismo, pero muestra latencia de red:

Cuando sepa lo que está bloqueando, con suerte el camino a la resolución será más claro.


Está utilizando Quartz, que administra los procesos temporizados, y esto parece tener lugar en momentos específicos.

Publique su cronograma de Quartz y avísenos si eso se alinea, y si es así, puede determinar qué proceso de aplicación interna puede iniciar para consumir sus recursos.

Alternativamente, es posible que una parte del código de su aplicación finalmente se haya activado y decida cargar datos en la memoria caché. Estás usando Hibernate; revise las llamadas a su base de datos y vea si algo coincide.


Tuvimos el mismo problema al ejecutar en Java 1.7.0_u101 (una de las versiones compatibles de Oracle, ya que el último JDK / JRE 7 público es 1.7.0_u79), ejecutándose en el recolector de basura G1. No puedo decir si el problema aparece en otras versiones de Java 7 o con otros GC.

Nuestro proceso fue Tomcat ejecutando Liferay Portal (creo que la versión exacta de Liferay no tiene ningún interés aquí).

Este es el comportamiento que observamos: al usar un -Xmx de 5GB, el tamaño inicial del grupo de caché de código inmediatamente después del inicio fue de aproximadamente 40 MB. Después de un tiempo, bajó a unos 30MB (lo cual es algo normal, ya que hay una gran cantidad de código ejecutándose durante el inicio que nunca se ejecutará de nuevo, por lo que se espera que se desactive de la memoria caché después de un tiempo). Observamos que había algo de actividad JIT, por lo que el JIT realmente llenó el caché (en comparación con los tamaños que menciono más adelante, parece que el pequeño tamaño del caché relativo al tamaño del montón global establece requisitos estrictos en el JIT, y esto hace que el este último desalojó el caché bastante nervioso). Sin embargo, después de un tiempo, no se produjeron más compilaciones, y la JVM se volvió dolorosamente lenta. Tuvimos que matar nuestros Tomcats de vez en cuando para recuperar el rendimiento adecuado, y cuando añadimos más código a nuestro portal, el problema empeoró cada vez más (ya que el Caché del Código se saturó más rápido, supongo).

Parece que hay varios errores en JDK 7 JVM que hacen que no se reinicie el JIT (mira esta publicación de blog: https://blogs.oracle.com/poonam/entry/why_do_i_get_message ), incluso en JDK 7, después de un lavado de emergencia (el blog menciona errores de Java 8006952, 8012547, 8020151 y 8029091).

Esta es la razón por la cual aumentar manualmente la caché de código a un nivel donde es poco probable que ocurra una descarga de emergencia "soluciona" el problema (supongo que este es el caso con JDK 7).

En nuestro caso, en lugar de tratar de ajustar el tamaño de la agrupación de caché de código, optamos por actualizar a Java 8. Esto parece haber solucionado el problema. Además, el Caché de código ahora parece ser bastante más grande (el tamaño de inicio es de aproximadamente 200 MB y el tamaño de crucero llega a unos 160 MB). Como es de esperar, después de un tiempo de inactividad, el tamaño del grupo de caché se reduce, para volver a levantarse si algún usuario (o robot, o lo que sea) navega por nuestro sitio, lo que hace que se ejecute más código.

Espero que encuentres útiles los datos anteriores.

Olvidé decir: encontré la exposición, los datos de apoyo, la lógica inferida y la conclusión de esta publicación muy, muy útil. ¡Gracias enserio!