[JVM] Explicación detallada de la memoria del montón de JVM (montón)

prefacio

La gestión de la memoria en montón de JAVA es uno de los principales factores que afectan al rendimiento.
El desbordamiento de la memoria del montón es una falla muy común en los proyectos JAVA.Antes de resolver este problema, primero debe comprender cómo funciona la memoria del montón de JAVA.

1. División de la memoria del montón

Primero observe cómo se divide la memoria del montón de JAVA, como se muestra en la figura:
inserte la descripción de la imagen aquí

  1. La memoria JVM se divide en memoria de pila y memoria que no es de pila. La memoria de pila se divide en generación joven (Generación joven) y generación anterior (Generación anterior). La memoria que no es de pila es una generación permanente (Generación permanente).
  2. La generación joven se divide en áreas Eden y Survivor. El área Survivor consta de FromSpace y ToSpace. El área Eden ocupa una gran capacidad y el área Survivor ocupa una capacidad pequeña. La proporción predeterminada es 8:1:1.
  3. Uso de la memoria del montón: los objetos se almacenan y el recolector de elementos no utilizados los recopila y luego los recicla de acuerdo con el algoritmo GC.
  4. Uso de memoria non-heap: La generación permanente, también conocida como área de métodos, almacena objetos que sobreviven durante mucho tiempo cuando el programa se está ejecutando, como metadatos, métodos, constantes, atributos, etc. de la clase.

En la versión JDK1.8, se abandona la generación permanente y el reemplazo es MetaSpace. El metaspace es similar a la generación permanente, y es la implementación del área de métodos. La mayor diferencia entre ellos es que el metaspace no está en la JVM, pero usa la memoria local.
Tenga en cuenta que el metaespacio tiene dos parámetros:

  • MetaspaceSize: inicializa el tamaño del metaespacio y controla el umbral de GC
  • MaxMetaspaceSize: limite el límite superior del tamaño del metaespacio para evitar que las excepciones ocupen demasiada memoria física

2. ¿Por qué eliminar la generación permanente?

Razón para eliminar la generación permanente: Cambios realizados para integrar HotSpot JVM y JRockit VM (nueva tecnología JVM), porque JRockit no tiene una generación permanente.
¡Con el meta espacio, ya no habrá problemas de OOM de generación permanente!

3. El concepto de generación

Los objetos recién generados se colocan primero en el área Eden de la generación joven. Cuando el espacio Eden está lleno, se activa un GC menor y los objetos sobrevivientes se mueven al área Survivor0. Después de que el área Survivor0 está llena, el Se activa el GC menor y los objetos sobrevivientes en el área Superviviente 0 se mueven al área Superviviente 1. Se garantiza que siempre haya un área de superviviente que esté vacía durante un período de tiempo. Los objetos que sobreviven a múltiples GC menores se mueven a la generación anterior.

La generación anterior almacena objetos sobrevivientes a largo plazo. Cuando esté lleno, activará Major GC = Full GC. Durante el GC, todos los subprocesos se detendrán y esperarán a que se complete el GC. Por lo tanto, las aplicaciones con altos requisitos de respuesta deben minimizar la ocurrencia de Major GC para evitar el tiempo de espera de respuesta.

  • Minor GC: limpiar la generación joven
  • Mayor GC: limpiar la vieja generación
  • GC completo: limpie todo el espacio del montón, incluida la generación joven y la generación permanente

Todo GC deja de aplicar todos los subprocesos.

Cuatro, ¿por qué generación?

Los objetos se clasifican según su probabilidad de supervivencia, y los objetos con un tiempo de supervivencia prolongado se colocan en un área fija, lo que reduce el tiempo de escaneo de basura y la frecuencia del GC. Realice diferentes algoritmos de recolección de basura para la clasificación y aproveche las fortalezas y evite las debilidades de los algoritmos.

5. ¿Por qué el sobreviviente se divide en dos espacios de sobreviviente de igual tamaño?

Principalmente para resolver la fragmentación. Si la fragmentación de la memoria es grave, es decir, dos objetos ocupan memoria discontinua y la memoria continua existente no es suficiente para almacenar nuevos objetos, se activará GC.

6. Parámetros comunes de la memoria del montón de JVM

parámetro describir
-Xms El tamaño inicial de la memoria del montón, en m, g
-Xmx(Tamaño Máx.Heap) El tamaño máximo permitido de la memoria del montón, generalmente no mayor al 80% de la memoria física
-XX: Tamaño permanente El tamaño inicial de la memoria que no es de montón, la configuración general de la aplicación se inicializa a 200m, y el máximo de 1024m es suficiente
-XX:TamañoMáxPerm Tamaño máximo permitido de memoria que no es de montón
-XX:NuevoTamaño(-Xns) Tamaño inicial de la memoria de la generación joven
-XX:MaxNewSize(-Xmn) El tamaño máximo permitido de la memoria de generación joven también se puede abreviar
-XX:Ratio de Supervivientes=8 La relación de capacidad del área Eden al área Survivor en la generación joven, el valor predeterminado es 8, es decir, 8: 1
-Xss tamaño de la memoria de pila

7. Algoritmo de recolección de basura (GC, recolección de basura)

El rojo es objetos inactivos marcados, el verde es objetos activos.

  • Mark-Sweep (Mark-Sweep)
    GC se divide en dos fases, marcar y borrar. Primero marque todos los objetos reciclables y recicle todos los objetos marcados de manera uniforme después de completar el marcado. Al mismo tiempo, se generará una fragmentación de memoria discontinua. La fragmentación excesiva hará que cuando el programa necesite asignar objetos grandes más tarde, no podrá encontrar suficiente memoria contigua, y el GC deberá activarse nuevamente como último recurso.
    inserte la descripción de la imagen aquí

  • Copiar (Copy)
    divide la memoria en dos bloques según la capacidad, y solo usa uno de ellos a la vez. Cuando se agote esta parte de la memoria, copie el objeto sobreviviente a otra parte y luego limpie el espacio de memoria usado a la vez. De esta manera, la mitad del área de la memoria se recupera cada vez y no hay necesidad de considerar el problema de la fragmentación de la memoria, que es simple y eficiente. La desventaja requiere el doble de espacio de memoria.
    inserte la descripción de la imagen aquí

  • Mark-compact (Mark-Compact)
    también se divide en dos etapas, primero marque los objetos reciclables, luego mueva todos los objetos sobrevivientes a un extremo y luego limpie la memoria fuera del límite. Este método evita el problema de fragmentación del algoritmo de barrido de marcas y también evita el problema de espacio del algoritmo de copia.
    Generalmente, después de que se ejecuta el GC en la generación joven, sobrevivirá una pequeña cantidad de objetos y se utilizará el algoritmo de copia, y la colección solo se puede completar pagando una pequeña cantidad del costo por copiar los objetos supervivientes. En la generación anterior, debido a que la tasa de supervivencia de los objetos es alta y no hay una asignación de espacio de memoria adicional, es necesario utilizar el algoritmo mark-clean o mark-compact para el reciclaje.
    ¡El montón de Java se desbordó de nuevo! enseñarte un nirvana
    inserte la descripción de la imagen aquí

8. Recolector de basura

  • El colector serial (Serial)
    es un colector más antiguo, de un solo subproceso. Al recopilar, los subprocesos de trabajo de la aplicación deben pausarse hasta que se complete la recopilación.

  • Recolector paralelo (Parallel)
    Varios subprocesos de recolección de elementos no utilizados funcionan en paralelo, lo que es más eficiente en CPU de varios núcleos, y los subprocesos de la aplicación aún están en estado de espera.

  • Colector CMS (Barrido de marcas concurrente)
    El colector CMS está diseñado para acortar el tiempo de pausa de la aplicación. Se implementa en base al algoritmo de barrido de marcas. Todo el proceso se divide en 4 pasos, que incluyen:

    • marca inicial
    • Marca concurrente
    • Observación
    • Barrido simultáneo

    Entre ellos, los dos pasos de marcado inicial y remarcado aún necesitan suspender el hilo de la aplicación. El marcado inicial es solo para marcar los objetos con los que GC Roots puede relacionarse directamente, y la velocidad es muy rápida. La fase de marcado concurrente es para marcar objetos reciclables, y la fase de remarcado es para corregir la parte de la marca que es cambiado debido a la operación continua del programa de usuario durante el período de marcado concurrente. El registro de marca del objeto, el tiempo de pausa de esta etapa es ligeramente más largo que la etapa de marca inicial, pero mucho más largo que el período de tiempo de marca concurrente.
    Dado que los subprocesos del recopilador del proceso de marcado y borrado simultáneos que consumen más tiempo en todo el proceso pueden trabajar junto con los subprocesos del usuario, la recuperación de memoria del recopilador CMS se ejecuta simultáneamente con el usuario, lo que reduce en gran medida el tiempo de pausa.

  • Recolector G1 (Garbage First)
    El recolector G1 divide la memoria del montón en varias regiones independientes (Regiones) de igual tamaño y puede predecir el tiempo de pausa y el motivo Puede evitar la recolección de área completa de todo el montón. G1 rastrea el valor de la acumulación de basura en cada Región (el tamaño del espacio adquirido y el tiempo requerido para el reciclaje), mantiene una lista de prioridades en segundo plano y da prioridad al reciclaje de la Región con el valor más alto de acuerdo con el tiempo de recolección permitido cada vez, asegurando así un tiempo limitado para una mayor eficiencia de recolección.
    La ingeniería de trabajo del colector G1 se divide en 4 pasos, que incluyen:

    • marca inicial
    • Marca concurrente
    • Nota Final
    • Recuperación de detección (conteo de datos en vivo y evacuación)

    La marca inicial es la misma que CMS, marca los objetos con los que GC Roots puede relacionarse directamente. El marcado simultáneo comienza desde GC Root para marcar los objetos supervivientes. Esta etapa lleva mucho tiempo, pero también se puede ejecutar simultáneamente con los subprocesos de la aplicación. La calificación final es también para corregir la parte del registro de calificación que se cambia debido a la operación continua del programa de usuario durante la calificación concurrente. Finalmente, clasifique el valor de recuperación y el costo de cada región en la etapa de recuperación de detección y realice la recuperación de acuerdo con el tiempo de pausa esperado del GC del usuario.

9. Parámetros del recolector de basura

parámetro describir
-XX:+UsarSerialGC coleccionista en serie
-XX:+UsarParallelGC colector paralelo
-XX:+Usar subprocesos GC paralelos=8 El número de subprocesos de recopilación paralelos, cuántos subprocesos realizan la recolección de basura al mismo tiempo, generalmente igual al número de CPU
-XX:+UsarParallelOldGC Designe la generación anterior para la recolección paralela
-XX:+UsarConcMarkSweepGC Colector CMS (recolector concurrente)
-XX:+UsarCMSCompactAtFullCollection Habilite la compresión y desfragmentación del espacio de memoria para evitar una fragmentación excesiva de la memoria
-XX:CMSFullGCsBeforeCompaction=0 Indica cuántas veces Full GC comienza a compactar y organizar, 0 significa realizar la compactación y organizar inmediatamente después de cada Full GC
-XX:CMSInitiatingOccupancyFraction=80% Indica que la recopilación de CMS comienza cuando el espacio de memoria de la generación anterior se usa en un 80 % para evitar un GC completo excesivo
-XX:+UsarG1GC colector G1
-XX:Umbral de tenencia máx.=0 Cuando la generación joven sobrevive a varios GC, ingresa a la generación anterior, y 0 significa que ingresa directamente a la generación anterior

10. ¿Por qué se desborda la memoria del montón?

Los objetos que sobreviven a GC en la generación joven se copian en la generación anterior. Cuando el espacio en la generación anterior es insuficiente, la JVM realizará una recolección de basura completa (GC completo) en la generación anterior. Si el objeto copiado del área Survivor no se puede almacenar después de GC, aparecerá OOM (Memoria insuficiente).

11. Las excepciones OOM (sin memoria) son comunes por las siguientes razones:

  • Memoria insuficiente en la generación anterior: java.lang.OutOfMemoryError: Javaheapspace
  • Memoria de generación permanente insuficiente: java.lang.OutOfMemoryError: PermGenspace
  • Los errores de código ocupan memoria y no se pueden recuperar a tiempo.

OOM puede aparecer en estas áreas de memoria.Cuando realmente encuentre OOM, puede ubicar el desbordamiento de memoria en qué área de acuerdo con la información de excepción.
Puede agregar un parámetro -XX:+HeapDumpOnOutMemoryError para permitir que la máquina virtual descargue la instantánea de volcado del montón de memoria actual para el análisis posterior cuando se produzca una excepción de desbordamiento de memoria.

Familiarizado con el mecanismo de administración de memoria JAVA y los parámetros de configuración, la siguiente es la configuración de ajuste de las opciones de inicio de la aplicación JAVA:

JAVA_OPTS="-server -Xms512m -Xmx2g -XX:+UseG1GC -XX:SurvivorRatio=6 -XX:MaxGCPauseMillis=400 -XX:G1ReservePercent=15 -XX:ParallelGCThreads=4 -XX:
ConcGCThreads=1 -XX:InitiatingHeapOccupancyPercent=40 -XX:+PrintGCDetails  -XX:+PrintGCTimeStamps -Xloggc:../logs/gc.log"
  • Establezca los valores mínimo y máximo de la memoria del montón, el valor máximo se refiere a la configuración de utilización histórica
  • Establezca el recolector de basura GC en G1
  • Habilitar registros de GC para análisis posteriores

resumen

  • La elección de un algoritmo de GC eficiente puede reducir efectivamente el tiempo para detener los subprocesos de la aplicación.
  • La GC completa frecuente aumentará el tiempo de pausa y el uso de la CPU. Puede aumentar el tamaño del espacio de la generación anterior para reducir la GC completa, pero aumentará el tiempo de recuperación y tomará las decisiones adecuadas según el negocio.

Supongo que te gusta

Origin blog.csdn.net/u011397981/article/details/130714618
Recomendado
Clasificación