Análisis y solución del aumento excesivo de memoria de una aplicación Spring Boot en un entorno Docker
Etiquetas relacionadas: 1. Preguntas frecuentes sobre errores de docker login, docker push y docker pull
2. Cómo instalar Docker en Ubuntu 20
Introducción a Spring Boot
Una aplicación sencilla de Spring Boot, usada por casi un solo usuario, alcanza 1.2G de memoria, lo cual es terrible
Una aplicación sencilla de Spring Boot, usada por casi un solo usuario, alcanza 1.2G de memoria, lo cual es terrible
Estado del servicio
Como había pocos servicios y los recursos del servidor eran suficientes, muchos servicios se iniciaban sin parámetros de la JVM (problemas heredados). El resultado es que cada arranque de servicio ocupa 1.2G-2G de memoria, y algunos servicios no llegan a usar tanta. Entonces, ¿cómo se configura la memoria de la JVM en Spring Boot si no se establece el parámetro de memoria de la JVM?
Al ejecutar un proyecto de Spring Boot, si no se establece el parámetro de memoria de la JVM, Spring Boot usa por defecto la estrategia de configuración predeterminada de la propia JVM. Cuando los recursos son suficientes, los desarrolladores no se preocupan mucho por la configuración de la memoria. Pero una vez que surgen la escasez de recursos y la optimización de la JVM, es necesario entender la estrategia de configuración de memoria predeterminada de la JVM.
Las configuraciones más comunes sobre la memoria de la JVM son el tamaño inicial del heap (- Xms ) y la memoria máxima del heap (- Xmx ). Muchos no se molestan en configurarlos y usan el valor predeterminado de la JVM. Sobre todo en el entorno de desarrollo, si se inician muchos microservicios, la memoria se sobrecarga.
Spring Boot.La estrategia de configuración de memoria predeterminada de la JVM se divide en dos escenarios: un escenario con un espacio de memoria amplio y otro con un espacio de memoria reducido (menos de 192M).
Si tomamos como ejemplo 4GB de memoria, el tamaño inicial de la memoria del heap y el tamaño máximo de la memoria del heap son los siguientes:
Spring Boot.Por defecto, la memoria máxima del heap ocupa 1/4 de la memoria física; si la aplicación supera este límite, se lanza una excepción OutOfMemoryError. El tamaño inicial de la memoria del heap es 1/64 de la memoria física.
Si la aplicación se ejecuta en un teléfono móvil o la memoria física es menor que 192M, el tamaño inicial predeterminado de la memoria del heap y el tamaño máximo de la memoria del heap de la JVM son los siguientes:
La memoria máxima del heap es 1/2 de la memoria física y el tamaño inicial de la memoria del heap es 1/64 de la memoria física, pero cuando la memoria inicial del heap es de al menos 8MB, será de 8MB.

Cuando la memoria libre predeterminada del heap es menor que 40 %, la JVM aumenta el heap hasta el límite máximo de - Xmx ; cuando la memoria libre del heap es mayor que 70 %, la JVM reduce el heap hasta el límite mínimo de - Xms .
Por lo tanto, en el servidor normalmente se establecen -Xms y -Xmx con el mismo valor para evitar redimensionar el heap después de cada GC. La memoria del heap de un objeto la recupera un sistema automático de gestión de memoria llamado recolector de basura.
La memoria máxima del heap es el límite superior de la memoria que usa la JVM y se emplea tanta como se necesita en el proceso de ejecución real. Por defecto, la cantidad máxima de espacio asignada a la generación joven es un tercio del tamaño total del heap.
Spring Boot.Para responder al problema inicial, si cada programa se inicia según la configuración predeterminada y varias aplicaciones se implementan en un servidor, habrá escasez de memoria, lo que genera cierto desperdicio. La operación más sencilla es agregar los parámetros de configuración de memoria de la jvm correspondientes al ejecutar el arranque con java -jar.
java -Xms64m -Xmx128m -jar xxx.jar
El proyecto usa la implementación con Docker; primero veamos el archivo Dockerfile original
EXPOSE 8061
ADD target/certif-system-2.1.0.jar /certif-system-2.1.0.jar
ENTRYPOINT ["java", "-jar","/certif-system-2.1.0.jar"]
Spring Boot.optimización
Limitar la memoria de la JVM
Spring Boot pasa todas las variables de entorno a la aplicación, pero nuestro JAVA_OPTS no es para la aplicación, sino para el propio tiempo de ejecución de Java. Por lo tanto, hay que usar la variable $JAVA_OPTS con exec java . Esto requiere algunos cambios pequeños en el Dockerfile:
ENTRYPOINT exec java $JAVA_OPTS -jar app.jar
Ejecute el comando docker run
Esto significa que la información del parámetro JAVA_OPTS en la variable de entorno se sobrescribe mediante el restablecimiento con -e en tiempo de ejecución.
docker run -e JAVA_OPTS='-Xmx1344M -Xms1344M -Xmn448M - XX:MaxMetaspaceSize =192M - XX:MetaspaceSize =192M'
Rol de java.security.egd
SecureRandom se usa ampliamente en varios componentes de java y puede generar números aleatorios de manera confiable. Sin embargo, en escenarios donde se genera una gran cantidad de números aleatorios, el rendimiento será menor. En ese momento, puede usar "- Djava.security.egd =file:/dev/./ urandom " para acelerar el proceso de generación de números aleatorios.
Se recomienda especificar el generador de números aleatorios como /dev/./urandom cuando se usan muchos números aleatorios.
Preste atención a la cuarta línea del código fuente que aparece a continuación para conocer la causa del defecto. Si el parámetro java.security.egd especifica file:/dev/random o file:/dev/urandom, se llama al constructor sin parámetros NativeSeedGenerator, y el constructor sin parámetros usará file:/dev/random por defecto. El código de openjdk es diferente del código de hotspot. Openjdk no usa esta variable al generar números aleatorios posteriormente.
2. Cómo instalar Docker en Ubuntu 20
Introducción a Spring Boot
Una aplicación sencilla de Spring Boot, usada por casi un solo usuario, alcanza 1.2G de memoria, lo cual es terrible
La memoria de la aplicación Spring Boot se dispara
Una aplicación sencilla de Spring Boot, usada por casi un solo usuario, alcanza 1.2G de memoria, lo cual es terrible
Estado del servicio
Como había pocos servicios y los recursos del servidor eran suficientes, muchos servicios se iniciaban sin parámetros de la JVM (problemas heredados). El resultado es que cada arranque de servicio ocupa 1.2G-2G de memoria, y algunos servicios no llegan a usar tanta. Entonces, ¿cómo se configura la memoria de la JVM en Spring Boot si no se establece el parámetro de memoria de la JVM?
Configuración de memoria predeterminada de Spring BootJVM
Al ejecutar un proyecto de Spring Boot, si no se establece el parámetro de memoria de la JVM, Spring Boot usa por defecto la estrategia de configuración predeterminada de la propia JVM. Cuando los recursos son suficientes, los desarrolladores no se preocupan mucho por la configuración de la memoria. Pero una vez que surgen la escasez de recursos y la optimización de la JVM, es necesario entender la estrategia de configuración de memoria predeterminada de la JVM.
Las configuraciones más comunes sobre la memoria de la JVM son el tamaño inicial del heap (- Xms ) y la memoria máxima del heap (- Xmx ). Muchos no se molestan en configurarlos y usan el valor predeterminado de la JVM. Sobre todo en el entorno de desarrollo, si se inician muchos microservicios, la memoria se sobrecarga.
Spring Boot.La estrategia de configuración de memoria predeterminada de la JVM se divide en dos escenarios: un escenario con un espacio de memoria amplio y otro con un espacio de memoria reducido (menos de 192M).
Si tomamos como ejemplo 4GB de memoria, el tamaño inicial de la memoria del heap y el tamaño máximo de la memoria del heap son los siguientes:
Spring Boot.Por defecto, la memoria máxima del heap ocupa 1/4 de la memoria física; si la aplicación supera este límite, se lanza una excepción OutOfMemoryError. El tamaño inicial de la memoria del heap es 1/64 de la memoria física.
Si la aplicación se ejecuta en un teléfono móvil o la memoria física es menor que 192M, el tamaño inicial predeterminado de la memoria del heap y el tamaño máximo de la memoria del heap de la JVM son los siguientes:
La memoria máxima del heap es 1/2 de la memoria física y el tamaño inicial de la memoria del heap es 1/64 de la memoria física, pero cuando la memoria inicial del heap es de al menos 8MB, será de 8MB.

Cuando la memoria libre predeterminada del heap es menor que 40 %, la JVM aumenta el heap hasta el límite máximo de - Xmx ; cuando la memoria libre del heap es mayor que 70 %, la JVM reduce el heap hasta el límite mínimo de - Xms .
Por lo tanto, en el servidor normalmente se establecen -Xms y -Xmx con el mismo valor para evitar redimensionar el heap después de cada GC. La memoria del heap de un objeto la recupera un sistema automático de gestión de memoria llamado recolector de basura.
La memoria máxima del heap es el límite superior de la memoria que usa la JVM y se emplea tanta como se necesita en el proceso de ejecución real. Por defecto, la cantidad máxima de espacio asignada a la generación joven es un tercio del tamaño total del heap.
Spring Boot.Para responder al problema inicial, si cada programa se inicia según la configuración predeterminada y varias aplicaciones se implementan en un servidor, habrá escasez de memoria, lo que genera cierto desperdicio. La operación más sencilla es agregar los parámetros de configuración de memoria de la jvm correspondientes al ejecutar el arranque con java -jar.
java -Xms64m -Xmx128m -jar xxx.jar
El proyecto usa la implementación con Docker; primero veamos el archivo Dockerfile original
EXPOSE 8061
ADD target/certif-system-2.1.0.jar /certif-system-2.1.0.jar
ENTRYPOINT ["java", "-jar","/certif-system-2.1.0.jar"]
Spring Boot.optimización
Limitar la memoria de la JVM
Spring Boot pasa todas las variables de entorno a la aplicación, pero nuestro JAVA_OPTS no es para la aplicación, sino para el propio tiempo de ejecución de Java. Por lo tanto, hay que usar la variable $JAVA_OPTS con exec java . Esto requiere algunos cambios pequeños en el Dockerfile:
ENTRYPOINT exec java $JAVA_OPTS -jar app.jar
Ejecute el comando docker run
Esto significa que la información del parámetro JAVA_OPTS en la variable de entorno se sobrescribe mediante el restablecimiento con -e en tiempo de ejecución.
docker run -e JAVA_OPTS='-Xmx1344M -Xms1344M -Xmn448M - XX:MaxMetaspaceSize =192M - XX:MetaspaceSize =192M'
Rol de java.security.egd
SecureRandom se usa ampliamente en varios componentes de java y puede generar números aleatorios de manera confiable. Sin embargo, en escenarios donde se genera una gran cantidad de números aleatorios, el rendimiento será menor. En ese momento, puede usar "- Djava.security.egd =file:/dev/./ urandom " para acelerar el proceso de generación de números aleatorios.
Se recomienda especificar el generador de números aleatorios como /dev/./urandom cuando se usan muchos números aleatorios.
Preste atención a la cuarta línea del código fuente que aparece a continuación para conocer la causa del defecto. Si el parámetro java.security.egd especifica file:/dev/random o file:/dev/urandom, se llama al constructor sin parámetros NativeSeedGenerator, y el constructor sin parámetros usará file:/dev/random por defecto. El código de openjdk es diferente del código de hotspot. Openjdk no usa esta variable al generar números aleatorios posteriormente.
Artículos relacionados
-
6 tecnologías opcionales de almacenamiento de datos
Equipo base de conocimiento
Ver más ofertas especiales
-
Short Message Service (SMS) y Mail Service
El paquete de 50,000 correos electrónicos comienza desde USD $1.99, 120 mensajes de texto desde solo USD $1.00
