Para la resolución de problemas de GC de servicio en línea, esto es suficiente

El problema de GC de los servicios en línea es un problema muy típico de los programas Java, que pone a prueba la capacidad del ingeniero para solucionar problemas. Al mismo tiempo, es una pregunta casi imprescindible para las entrevistas, pero no hay muchas personas que realmente puedan responder a esta pregunta: o no comprenden bien los principios o carecen de experiencia práctica.

En los últimos seis meses, nuestro sistema de publicidad ha experimentado muchos problemas en línea relacionados con GC. La GC completa es demasiado frecuente y Young GC tarda demasiado. El impacto de estos problemas es: el programa en el proceso de GC Parada, lo que genera más horas extraordinarias de servicio y, por lo tanto, afecta los ingresos por publicidad.

En este artículo, utilizaré un caso frecuente en línea de FGC como introducción para presentar en detalle el proceso de resolución de problemas de GC. Además, brindaré una guía práctica basada en el principio operativo de GC, que espero le ayude. El contenido se divide en las siguientes 3 partes:

  • Hablando de un caso frecuente en línea de FGC
  • Introducción al principio de funcionamiento de GC
  • Una guía práctica para solucionar problemas de FGC

 

01 Hablando de un caso frecuente en línea de FGC

En octubre del año pasado, nuestro sistema de retiro de publicidad recibió alertas frecuentes del sistema de FGC después del lanzamiento del programa. Puede ver en la siguiente tabla de monitoreo: FGC se realiza cada 35 minutos en promedio. Antes de que el programa saliera a la luz, nuestra frecuencia de FGC era aproximadamente una vez cada 2 días. A continuación, presentaremos el proceso de solución de problemas de este problema en detalle.

Para la resolución de problemas de GC de servicio en línea, esto es suficiente

 

1. Verifique la configuración de JVM

Vea los parámetros de inicio de la JVM con el siguiente comando:

ps aux | grep "applicationName = adsearch"

-Xms4g -Xmx4g -Xmn2g -Xss1024K

-XX: ParallelGCThreads = 5

-XX: + UseConcMarkSweepGC

-XX: + UseParNewGC

-XX: + UseCMSCompactAtFullCollection

-XX: CMSInitiatingOccupancyFraction = 80

Se puede ver que la memoria del montón es 4G, la nueva generación es 2G, la generación anterior también es 2G, la nueva generación usa el colector ParNew y la generación anterior usa el recolector CMS con eliminación simultánea de marcas. Cuando el uso de memoria de la generación anterior alcanza el 80%, se realizará FGC. .

Además, a través de jmap -heap 7276 | head -n20, podemos saber que el área de Eden de la nueva generación es 1.6G, y las áreas S0 y S1 son ambas 0.2G.

2. Observa los cambios de memoria en la vejez.

Al observar el uso de la generación anterior, podemos ver que después de cada FGC, la memoria puede volver a aproximadamente 500M, por lo que hemos descartado la pérdida de memoria.

Para la resolución de problemas de GC de servicio en línea, esto es suficiente

 

3. Vea los objetos en la memoria del montón a través del comando jmap

Mediante el comando jmap -histo 7276 | head -n20

Para la resolución de problemas de GC de servicio en línea, esto es suficiente

 

En la figura anterior, ordenada por el tamaño de la memoria de los objetos, muestra el número de instancias de objetos supervivientes, la memoria ocupada y el nombre de la clase. Puede ver que el primero es: int [], y el tamaño de la memoria es mucho mayor que el de otros objetos activos. En este punto, hemos bloqueado el objetivo sospechoso en int [].

4. Volcado adicional de archivos de memoria dinámica para su análisis

Después de bloquear int [], planeamos volcar el archivo de memoria del montón y seguir el origen del objeto a través de herramientas de visualización. Teniendo en cuenta que el programa se suspenderá durante el volcado de pila, primero eliminamos este nodo de la plataforma de administración de servicios y luego volcamos la memoria de pila mediante el siguiente comando:

jmap -dump:format=b,file=heap 7276

Importa el archivo de memoria del montón volcado a través de la herramienta JVisualVM, también puedes ver el espacio que ocupa cada objeto, donde int [] ocupa más del 50% de la memoria, más abajo puedes encontrar el objeto comercial al que pertenece int [], y encontrar que proviene de Los componentes básicos de codis proporcionados por el equipo de arquitectura.

Para la resolución de problemas de GC de servicio en línea, esto es suficiente

 

5. Analizar objetos sospechosos mediante código

A través del análisis de código, los componentes básicos de codis generarán una matriz int con un tamaño de aproximadamente 40 M cada minuto, que se utiliza para contar TP99 y TP90. El ciclo de vida de la matriz es de un minuto. De acuerdo con la observación de los cambios de memoria de la vieja generación en el paso 2, se encuentra que la memoria de la vieja generación básicamente aumenta en más de 40M por minuto, por lo que se infiere que la matriz int de 40M debe promoverse de la nueva generación a la vieja generación.

Además, verificamos el monitoreo de frecuencia de YGC. En la figura siguiente, podemos ver que hay alrededor de 8 YGC por minuto, lo que básicamente verifica nuestra inferencia: porque la edad de generación predeterminada del colector CMS es 6 veces, es decir, YGC 6 veces Los objetos supervivientes serán promovidos a la vejez, y el ciclo de vida de la matriz grande en el componente codis es de 1 minuto, que solo cumple con este requisito.

Para la resolución de problemas de GC de servicio en línea, esto es suficiente

 

En este punto, todo el proceso de investigación básicamente ha terminado, entonces, ¿por qué no ocurrió este problema antes de que el programa se pusiera en marcha? En la figura anterior, podemos ver que la frecuencia de YGC era aproximadamente 5 veces antes de que se lanzara el programa, y ​​la frecuencia de YGC se convirtió en aproximadamente 8 después de que se lanzó el programa, lo que causó este problema.

6. Solución

Para resolver rápidamente el problema, cambiamos la edad de generación del colector CMS a 15 veces. Después del cambio, la frecuencia de FGC volvió a una vez cada 2 días. Si la frecuencia de YGC excede las 15 veces por minuto, este problema se activará nuevamente. Por supuesto, nuestra solución más fundamental es: optimizar el programa para reducir la frecuencia de YGC, mientras se acorta el ciclo de vida de la matriz int en el componente codis, no lo expandiremos aquí.

 

02 Introducción al principio de funcionamiento de GC

El proceso de análisis de todo el caso anterior en realidad implica mucho conocimiento de los principios de GC. Si no comprende estos principios, puede comenzar a tratar con ellos. De hecho, todo el proceso de investigación es muy ciego.

Aquí, elijo algunos puntos de conocimiento básicos, comienzo a presentar el principio operativo de GC y finalmente doy una guía práctica.

1. Estructura de memoria dinámica

Todo el mundo lo sabe: GC se divide en YGC y FGC, los cuales se encuentran en la memoria de pila de la JVM. Primero mire la estructura de la memoria del montón de JDK8:

Para la resolución de problemas de GC de servicio en línea, esto es suficiente

 

Se puede ver que la memoria del montón adopta una estructura generacional, incluyendo la nueva generación y la vieja generación. La nueva generación se divide en: área de Edén, desde el área de Superviviente (S0 para abreviar), hasta el área de Superviviente (S1 para abreviar), la proporción predeterminada de los tres es 8: 1: 1. Además, la proporción predeterminada entre la generación joven y la generación anterior es de 1: 2.

La razón por la que la memoria dinámica adopta la estructura generacional es que la mayoría de los objetos tienen ciclos de vida cortos, por lo que los objetos con diferentes ciclos de vida se pueden colocar en diferentes áreas, y luego se utilizan diferentes algoritmos de recolección de basura para la nueva generación y la generación anterior. , Lo que hace que GC sea el más eficiente.

2. ¿Cuándo se activa YGC?

En la mayoría de los casos, los objetos se asignan directamente en el área del Edén de la generación joven. Si el área del Edén no tiene suficiente espacio, entonces se activará YGC (Minor GC). El área procesada por YGC es solo la generación joven. Debido a que la mayoría de los objetos se pueden recuperar en poco tiempo, solo unos pocos objetos pueden sobrevivir después de YGC y se mueven al área S0 (usando un algoritmo de copia).

Cuando se activa el siguiente YGC, los objetos supervivientes en el área Edén y el área S0 se mueven al área S1, y el área Edén y el área S0 se limpian al mismo tiempo. Cuando YGC se activa nuevamente, el área procesada en este momento se convierte en el área Edén y el área S1 (es decir, S0 y S1 intercambian roles). Cada vez que pasa YGC, la edad del objeto sobreviviente aumentará en 1.

3. ¿Cuándo se activa la FGC?

El sujeto ingresará a la vejez en las siguientes cuatro situaciones:

  • En YGC, el área To Survivor no es suficiente para almacenar objetos supervivientes, y los objetos entrarán directamente en la vejez.
  • Después de muchos YGC, si la edad del objeto sobreviviente alcanza el umbral establecido, se promoverá a la vejez.
  • Reglas de determinación de edad dinámica. Si los objetos de la misma edad en el área Al superviviente ocupan más de la mitad del espacio en el área Al superviviente, los objetos mayores de esta edad entrarán directamente en la vejez sin alcanzar la edad generacional predeterminada. .
  • Objetos grandes: controlado por el parámetro de inicio -XX: PretenureSizeThreshold. Si el tamaño del objeto es mayor que este valor, pasará por alto la generación joven y se asignará directamente a la generación anterior.

Cuando los objetos promovidos a la vejez sean más grandes que el espacio restante en la vejez, se activará FGC (Major GC) El área procesada por FGC incluye tanto a los jóvenes como a los mayores. Además, hay 4 situaciones que desencadenarán FGC:

  • El uso de la memoria en la vejez alcanza un cierto umbral (ajustable por parámetro), que dispara directamente FGC.
  • Garantía de asignación de espacio: antes de YGC, primero verificará si el espacio continuo máximo disponible en la generación anterior es mayor que el espacio total de todos los objetos en la nueva generación. Si es menor que, significa que YGC no es seguro. Verificará si el parámetro HandlePromotionFailure está configurado para permitir falla de garantía. Si no está permitido, activará directamente Full GC; si está permitido, verificará si el espacio continuo máximo disponible en la generación anterior es mayor que en tiempos anteriores. Promocionado al tamaño medio de los objetos de la generación anterior, si es más pequeño, activará la GC completa.
  • El metaespacio (metaespacio) se expandirá cuando el espacio sea insuficiente. Cuando la expansión alcance el valor especificado de -XX: parámetro MetaspaceSize, también se activará FGC.
  • Cuando se llama explícitamente a System.gc () o Runtime.gc (), se activa FGC.

4. ¿Bajo qué circunstancias afectará GC al programa?

Independientemente de YGC o FGC, causará un cierto grado de retraso del programa (problema de Stop The World: el subproceso GC comienza a funcionar, otros subprocesos de trabajo están suspendidos), incluso si se utilizan algoritmos de recolección de basura más avanzados como ParNew, CMS o G1, es solo Al reducir el tiempo de retraso, no puede eliminarlo por completo.

Entonces, ¿bajo qué circunstancias afectará GC al programa? Según la gravedad de mayor a menor, creo que se incluyen las siguientes 4 situaciones:

  • FGC es demasiado frecuente: FGC suele ser lento, desde varios cientos de milisegundos hasta varios segundos, normalmente, FGC se ejecuta cada pocas horas o incluso días, y el impacto en el sistema es aceptable. Sin embargo, una vez que FGC ocurre con frecuencia (por ejemplo, se ejecutará cada decenas de minutos), esto definitivamente es un problema. Hará que el hilo de trabajo se detenga con frecuencia, haciendo que el sistema parezca estar bloqueado, y también hará que el programa general El rendimiento se deteriora.
  • YGC tarda demasiado: En general, es normal que el tiempo total de YGC sea de decenas o cientos de milisegundos. Aunque provocará que el sistema se congele durante varios milisegundos o decenas de milisegundos, esta situación es casi insensible para los usuarios. La influencia del procedimiento es insignificante. Pero si YGC tarda 1 segundo o incluso algunos segundos (casi para ponerse al día con el tiempo de FGC), el tiempo de bloqueo aumentará y YGC en sí es más frecuente, lo que provocará más problemas de tiempo de espera del servicio.
  • FGC consume mucho tiempo: aumenta el tiempo de FGC y el tiempo de parada también aumentará, especialmente para los servicios de alta concurrencia, que pueden causar más problemas de tiempo de espera durante FGC y una disponibilidad reducida. Esto también requiere atención.
  • YGC es demasiado frecuente: incluso si YGC no causa tiempos de espera de servicio, YGC demasiado frecuente reducirá el rendimiento general del servicio. También requiere atención para los servicios de alta concurrencia.

Entre ellos, "FGC es demasiado frecuente" y "YGC tarda demasiado", estas dos situaciones son problemas de GC más típicos, que probablemente afectarán la calidad del servicio del programa. Los dos casos restantes son menos graves, pero también es necesario prestar atención a programas de alta concurrencia o alta disponibilidad.

 

03 Una guía práctica para solucionar problemas de FGC

A través del análisis de caso anterior y la introducción teórica, resumiremos las ideas de resolución de problemas para problemas de FGC, como una guía práctica para su referencia.

1. Desde el punto de vista del programa, ¿qué causa la FGC?

  • Objetos grandes: el sistema carga demasiados datos en la memoria a la vez (por ejemplo, las consultas SQL no se paginan), lo que hace que los objetos grandes entren en la vejez.
  • Pérdida de memoria: con frecuencia se crea una gran cantidad de objetos, pero no se pueden reciclar (por ejemplo, no se llama al método de cierre para liberar recursos después de que se agota el objeto IO), primero se activa FGC y finalmente OOM.
  • El programa genera con frecuencia algunos objetos longevos. Cuando la edad de supervivencia de estos objetos supere la edad generacional, entrarán en la vejez y finalmente desencadenarán FGC (el caso de este artículo).
  • El programa BUG conduce a la generación dinámica de muchas clases nuevas, haciendo que Metaspace esté constantemente ocupado, primero activando FGC y finalmente conduciendo a OOM.
  • El método gc se llama explícitamente en el código, incluido su propio código e incluso el código en el marco.
  • Problemas de configuración de parámetros de JVM: incluido el tamaño total de la memoria, el tamaño de las generaciones jóvenes y mayores, el tamaño del área Eden y el área S, el tamaño del metaespacio, el algoritmo de recolección de basura, etc.

 

2. Sepa qué herramientas puede utilizar para solucionar problemas

  • El sistema de monitoreo de la empresa: la mayoría de las empresas lo tendrán, que puede monitorear todos los indicadores de la JVM.
  • Las propias herramientas de JDK, incluidos los comandos de uso común como jmap, jstat: # Ver el uso de cada área de la memoria del montón y el estado del GC jstat -gcutil -h20 pid 1000 # Ver los objetos activos en la memoria del montón y ordenarlos por espacio jmap -histo pid | head -n20 # volcar archivo de memoria de pila jmap -dump: format = b, file = heap pid
  • Herramientas de análisis de memoria de pila visual: JVisualVM, MAT, etc.

 

3. Guía de resolución de problemas

  • Verifique el monitoreo para comprender el momento del problema y la frecuencia del FGC actual (puede comparar la situación normal para ver si la frecuencia es normal)
  • Comprenda si hay programas en línea, actualizaciones de componentes básicos, etc. antes de ese momento.
  • Comprenda la configuración de los parámetros de JVM, incluida la configuración de tamaño de cada área del espacio de almacenamiento dinámico, qué recolectores de basura se utilizan en la nueva generación y la generación anterior, y luego analice si la configuración de los parámetros de JVM es razonable.
  • Luego, elimine las posibles causas enumeradas en el paso 1. Entre ellas, el metaespacio está lleno, pérdidas de memoria y el código llama explícitamente al método gc. Es más fácil de solucionar.
  • Para FGC causado por objetos grandes u objetos de larga duración, el comando jmap -histo se puede usar junto con el archivo de memoria de volcado de memoria para un análisis más detallado, y los objetos sospechosos deben ubicarse primero.
  • Vuelva a analizar el código específico localizando el objeto sospechoso. En este momento, es necesario combinar el principio de GC y la configuración de los parámetros de JVM para averiguar si el objeto sospechoso cumple con las condiciones para ingresar a la vejez y sacar una conclusión.

Ultimas palabras

Este artículo presenta el proceso de resolución de problemas de FGC en detalle a través de casos en línea combinados con los principios de GC, y también ofrece una guía práctica.

En el seguimiento, compartiré otro caso de YGC que lleva demasiado tiempo de manera similar. Espero que pueda ayudarlo a comprender la solución de problemas de GC. Si cree que este artículo es útil para usted, ayúdenos a avanzar o haga clic para leerlo nuevamente.

Reimpreso en: https://mp.weixin.qq.com/s/Hs2bo37x7mcx7XTdNQVgZQ

Supongo que te gusta

Origin blog.csdn.net/qq_45401061/article/details/108761500
Recomendado
Clasificación