Una práctica de ajuste de JVM en línea, el proceso de optimización de FullGC40 veces al día a una vez cada 10 días

A través de más de un mes de arduo trabajo, el FullGC se ha optimizado de 40 veces al día a casi 10 días, y el tiempo de YoungGC se ha reducido a más de la mitad. Para una optimización tan grande, es necesario registrar la afinación intermedia. proceso.

Para la recolección de basura JVM, siempre ha estado en la etapa teórica antes, y conocemos la relación de promoción entre la nueva generación y la generación anterior.Este conocimiento solo es suficiente para las entrevistas. Hace algún tiempo, el FullGC del servidor en línea era muy frecuente, con un promedio de más de 40 veces al día, y el servidor se reiniciaba automáticamente cada pocos días, lo que demuestra que el estado del servidor es muy anormal. buena oportunidad, por supuesto, debe tomar la iniciativa Se solicita Tuning. Los datos del GC del servidor antes del ajuste, FullGC son muy frecuentes.

En primer lugar, la configuración del servidor es muy general (2 núcleos 4G), un total de 4 clústeres de servidores. El número y tiempo de FullGC para cada servidor son básicamente los mismos. Entre ellos, los parámetros de inicio de varios núcleos JVM son:

-Xms1000M -Xmx1800M -Xmn350M -Xss300K -XX:+DisableExplicitGC -XX:SurvivorRatio=4 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:LargePageSizeInBytes=128M -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC

-Xmx1800M: establezca la memoria máxima disponible de la JVM en 1800M.
-Xms1000m: establezca la memoria de inicialización de JVM en 1000m. Este valor se puede establecer igual que -Xmx para evitar que JVM reasigne memoria después de que se complete cada recolección de elementos no utilizados.
-Xmn350M: establezca el tamaño de la generación joven en 350M. El tamaño completo de la memoria JVM = tamaño de generación joven + tamaño de generación anterior + tamaño de generación permanente. La generación permanente generalmente tiene un tamaño fijo de 64 m, por lo que aumentar la generación joven reducirá el tamaño de la generación anterior. Este valor tiene un gran impacto en el rendimiento del sistema y Sun recomienda oficialmente configurarlo como 3/8 del montón completo.
-Xss300K: establece el tamaño de la pila para cada subproceso. Después de JDK5.0, el tamaño de la pila de cada subproceso es de 1 M, y antes el tamaño de la pila de cada subproceso era de 256 K. Ajuste el tamaño de memoria requerido por más subprocesos de aplicación. Bajo la misma memoria física, reducir este valor puede generar más hilos. Sin embargo, el sistema operativo todavía tiene un límite en la cantidad de subprocesos en un proceso y no se puede generar infinitamente. El valor de la experiencia es de alrededor de 3000~5000.

primera optimización

Mirando los parámetros, inmediatamente siento por qué la nueva generación es tan pequeña y cómo mejorar el rendimiento si es tan pequeño, y causará la activación frecuente de YoungGC. La colección de la nueva generación como la anterior toma 830 s. La memoria de pila inicial no es consistente con la memoria de pila máxima. Después de consultar varios materiales, se recomienda establecer estos dos valores para que sean iguales, lo que puede evitar la reasignación de memoria después de cada GC. Con base en el conocimiento previo, se llevó a cabo el primer ajuste en línea: aumentar el tamaño de la nueva generación y establecer la memoria de almacenamiento dinámico inicial en la memoria máxima

-Xmn350M -> -Xmn800M
-XX:SurvivorRatio=4 -> -XX:SurvivorRatio=8
-Xms1000m ->-Xms1800m

La intención original de cambiar SurvivorRatio a 8 es permitir que se recicle la mayor cantidad de basura posible en la nueva generación. De esta manera, después de implementar la configuración en dos servidores en línea (prod, prod2, los otros dos permanecen sin cambios para comparar), después de ejecutar durante 5 días, observando los resultados de GC, el número de YoungGC se ha reducido a más de la mitad, y el tiempo se ha reducido en 400 s, pero FullGC El número promedio de veces aumentó en 41 veces. YoungGC está básicamente en línea con las expectativas, pero este FullGC está completamente muerto.

De esta forma, la primera optimización falló.

Segunda optimización

Durante el proceso de optimización, nuestro supervisor descubrió que hay más de 10 000 instancias de un objeto T en la memoria, y estas instancias ocupan casi 20 millones de memoria. Entonces, de acuerdo con el uso de este objeto bean, el motivo se encontró en el proyecto: causado por una referencia de clase interna anónima, el pseudocódigo es el siguiente:

public void doSmthing(T t){
	redis.addListener(new Listener(){
		public void onTimeout(){
			if(t.success()){
				//执行操作
			}
		}
	});
}

Dado que el oyente no se liberará después de la devolución de llamada, y la devolución de llamada es una operación de tiempo de espera, la devolución de llamada solo se realizará cuando un evento supere el tiempo establecido (1 minuto), lo que hará que el objeto T nunca se recicle, por lo que el memory Habrá tantas instancias de objetos en .
Después de descubrir una pérdida de memoria a través del ejemplo anterior, primero verifique el archivo de registro de errores en el programa y primero resuelva todos los eventos de error. Luego, después del relanzamiento, la operación de GC permaneció básicamente sin cambios. Aunque se resolvió un pequeño problema de fuga de memoria, se puede explicar que la causa raíz no se resolvió y el servidor continuó reiniciando inexplicablemente.

investigación de fuga de memoria

Después del primer ajuste, se encontró un problema de fuga de memoria, por lo que todos comenzaron a investigar la fuga de memoria y primero verificaron el código, pero esta eficiencia fue bastante baja y básicamente no se encontraron problemas. Así que continué volcando la memoria cuando la línea no estaba muy ocupada y finalmente atrapé un objeto grande.

Hay más de 4 objetos W, y todos son objetos ByteArrowRow. Se puede confirmar que estos datos se generan durante la consulta o inserción de la base de datos. Entonces se llevó a cabo otra ronda de análisis de código. Durante el análisis de código, los colegas de operación y mantenimiento encontraron que el tráfico de entrada se había duplicado varias veces en un momento determinado del día, y era tan alto como 83 MB/s. Después de algunos confirmación, no existe tal cosa en la actualidad. Hay una gran cantidad de negocios, y no hay función de carga de archivos. Consultar el servicio al cliente de Alibaba Cloud también mostró que el tráfico es completamente normal y se puede descartar la posibilidad de un ataque.

Mientras aún estaba investigando el problema del tráfico de entrada, otro colega encontró la causa raíz. Resultó que, bajo cierta condición, se consultarían todos los datos especificados sin procesar en la tabla, pero debido a que la condición where se agregaba menos al consultar la condición. del módulo se cumple, lo que resulta en más de 400,000 consultas, y al ver las solicitudes y los datos en ese momento a través del registro, se puede juzgar que esta lógica de hecho se ha ejecutado. Solo hay más de 4 W objetos en la memoria volcada Esto se debe a que se consultaron muchos durante el volcado y el resto aún se estaba transmitiendo. Y esto también puede explicar muy bien por qué el servidor se reiniciará automáticamente.

Después de solucionar este problema, el servidor en línea está funcionando con total normalidad.Con los parámetros antes del ajuste, ha estado funcionando durante aproximadamente 3 días y solo hay 5 veces de FullGC.

Segunda afinación

El problema de las fugas de memoria se resolvió y el resto se puede ajustar. Después de verificar el registro de GC, se descubrió que durante los primeros tres GullGC, la memoria ocupada por la generación anterior era inferior al 30%, pero se produjo FullGC. Así que realicé una encuesta de varios materiales. En el blog https://blog.csdn.net/zjwstz/article/details/77478054, es muy claro y claro que el metaespacio conduce a FullGC. El metaespacio predeterminado del servidor es 21 M. En el registro de GC Al ver que el metaespacio ocupaba alrededor de 200 M cuando era el más grande, realizamos los siguientes ajustes. Los siguientes son los parámetros modificados de prod1 y prod2, y prod3 y prod4 permanecen sin cambios

-Xmn350M -> -Xmn800M
-Xms1000M ->1800M
-XX:MetaspaceSize=200M
-XX:CMSInitiatingOccupancyFraction=75

y

-Xmn350M -> -Xmn600M
-Xms1000M ->1800M
-XX:MetaspaceSize=200M
-XX:CMSInitiatingOccupancyFraction=75

prod1 y 2 solo son diferentes en el tamaño de la nueva generación, y los demás son iguales. Se ha estado ejecutando en línea durante unos 10 días para comparar:
prod1:

prod2:

prod3:

prod4:

En comparación, el FullGC de los dos servidores 1 y 2 es mucho más bajo que el de los dos servidores 3 y 4, y el YoungGC de los dos servidores 1 y 2 también se reduce a la mitad en comparación con el 3 y 4, y la eficiencia del primer servidor es más obvio Además de la reducción en el número de YoungGC, y el rendimiento es mayor que el de 3 y 4 que se han estado ejecutando durante un día más (a través del número de inicios de subprocesos), muestra que el rendimiento de prod1 se mejora particularmente.

A través de la cantidad de tiempos de GC y el tiempo de GC, esta optimización se declara exitosa y la configuración de prod1 es mejor, lo que mejora en gran medida el rendimiento del servidor y reduce el tiempo de GC a más de la mitad .

El único FullGC en prod1:

No puedo ver el motivo a través del registro de GC. La generación anterior solo ocupaba alrededor de 660M cuando cms comentó. Esta no debería ser la condición para activar FullGC, y a través de las investigaciones anteriores de YoungGC, también se descartó la promoción de objetos de gran memoria. Es posible que el tamaño del metaespacio no cumpla las condiciones de GC. Esto aún debe investigarse más a fondo. Cualquiera que sepa puede señalarlo. Gracias de antemano.

Resumir

A través de este más de un mes de puesta a punto, se resumen los siguientes puntos:

  • FullGC más de una vez al día definitivamente no es normal.
  • Cuando se descubre que FullGC es frecuente, la prioridad es investigar las fugas de memoria.
  • Una vez que se resuelve la fuga de memoria, hay menos espacio para el ajuste de jvm, lo cual está bien para el aprendizaje; de ​​lo contrario, no invierta demasiado tiempo.
  • Si encuentra que la CPU continúa siendo alta, puede consultar el servicio al cliente de operación y mantenimiento de Alibaba Cloud después de solucionar el problema del código. Durante esta investigación, se encontró que el 100% de la CPU fue causada por un problema del servidor. Después la migración del servidor, será normal.
  • La consulta de datos también se cuenta como el tráfico de entrada del servidor.Si no hay una gran cantidad de negocios de acceso y no hay un problema de ataque, puede investigar la base de datos.
  • Es necesario prestar atención al GC del servidor de vez en cuando, para que los problemas se puedan encontrar temprano.

Supongo que te gusta

Origin blog.csdn.net/agonie201218/article/details/131826428
Recomendado
Clasificación