Análise e solução do aumento de memória de aplicação Spring Boot em ambiente Docker

Tags relacionadas:1. FAQ sobre erros de docker login, docker push e docker pull
2. Como instalar o Docker no Ubuntu 20

Introdução ao Spring Boot

Uma aplicação Spring Boot simples usada por quase apenas um usuário atinge 1,2 GB de memória, o que é preocupante.

Aumento excessivo de memória em aplicação Spring Boot


Uma aplicação Spring Boot simples usada por quase apenas um usuário atinge 1,2 GB de memória, o que é preocupante.
Status do serviço

Como havia poucos serviços e recursos de servidor suficientes, muitos serviços foram iniciados sem parâmetros JVM (problemas herdados). O resultado é que cada serviço ocupa de 1,2 GB a 2 GB de memória na inicialização, e alguns serviços nem precisam de tanto. Então, como a memória JVM é configurada no Spring Boot quando nenhum parâmetro de memória JVM é definido?

Configurações padrão de memória JVM no Spring Boot


Ao executar um projeto Spring Boot, se nenhum parâmetro de memória JVM for definido, o Spring Boot usará a estratégia de configuração padrão da própria JVM. Quando há recursos suficientes, os desenvolvedores geralmente não se preocupam com as configurações de memória. Mas quando surge escassez de recursos e é necessária a otimização da JVM, é preciso entender a estratégia padrão de configuração de memória JVM.
As configurações mais comuns de memória JVM são o tamanho inicial do heap (-Xms) e a memória heap máxima (-Xmx). Muitos optam por não configurá-las e usam o valor padrão da JVM. Especialmente em ambientes de desenvolvimento, se muitos microsserviços forem iniciados, a memória será esgotada.

A estratégia padrão de configuração de memória JVM do Spring Boot divide-se em dois cenários: cenário de espaço de memória grande e cenário de espaço de memória pequeno (menos de 192 MB).
Tomando 4 GB de memória como exemplo, o tamanho inicial do heap e o tamanho máximo do heap são os seguintes:

Por padrão, a memória heap máxima ocupa 1/4 da memória física; se a aplicação ultrapassar esse limite, uma exceção OutOfMemoryError será lançada. O tamanho inicial do heap é 1/64 da memória física.
Se a aplicação for executada em um dispositivo móvel ou a memória física for inferior a 192 MB, os valores padrão de heap inicial e heap máximo da JVM serão os seguintes:

A memória heap máxima é 1/2 da memória física, e o tamanho inicial do heap é 1/64 da memória física, mas quando o heap inicial for de pelo menos 8 MB, ele será definido como 8 MB.

Quando a memória heap livre padrão for inferior a 40%, a JVM aumentará o heap até o limite máximo de -Xmx; quando a memória heap livre for superior a 70%, a JVM reduzirá o heap até o limite mínimo de -Xms.
Por isso, nos servidores geralmente se define -Xms e -Xmx com valores iguais para evitar redimensionamento do heap após cada GC. A memória heap de um objeto é recuperada por um sistema automático de gerenciamento de memória chamado coletor de lixo.

A memória heap máxima é o limite superior da memória utilizada pela JVM, e o consumo real depende do processo de execução. Por padrão, o espaço máximo alocado à geração jovem é um terço do tamanho total do heap.

Em resposta ao problema inicial, se cada programa for iniciado com a configuração padrão, quando várias aplicações forem implantadas em um único servidor, haverá escassez de memória, resultando em desperdício. A solução mais simples é adicionar os parâmetros correspondentes de configuração de memória JVM ao executar java -jar para iniciar.
java -Xms64m -Xmx128m -jar xxx.jar
O projeto usa implantação com Docker; vejamos primeiro o arquivo 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"]

Otimização do Spring Boot
Limitar a memória JVM

O Spring Boot transmite todas as variáveis de ambiente para a aplicação — mas nosso JAVA_OPTS não é destinado à aplicação, e sim ao próprio runtime Java. Portanto, precisamos usar a variável $JAVA_OPTS com exec java. Isso requer algumas pequenas alterações no Dockerfile:
ENTRYPOINT exec java $JAVA_OPTS -jar app.jar
Executar o comando docker run
Isso significa que as informações do parâmetro JAVA_OPTS na variável de ambiente são substituídas por -e redefinido em tempo de execução.
docker run -e JAVA_OPTS='-Xmx1344M -Xms1344M -Xmn448M -XX:MaxMetaspaceSize=192M -XX:MetaspaceSize=192M'

Papel de java.security.egd
O SecureRandom é amplamente utilizado em vários componentes do Java e gera números aleatórios de forma confiável. No entanto, em cenários que exigem grande volume de números aleatórios, o desempenho pode ser reduzido. Nesses casos, é possível usar "-Djava.security.egd=file:/dev/./urandom" para acelerar o processo de geração de números aleatórios.
Recomenda-se especificar o gerador de números aleatórios como /dev/./urandom quando houver uso intensivo de números aleatórios.
Observe a quarta linha do código-fonte abaixo para entender a causa do bug. Se o parâmetro java.security.egd especificar file:/dev/random ou file:/dev/urandom, o construtor NativeSeedGenerator sem parâmetros será chamado, e esse construtor usará file:/dev/random por padrão. O código do OpenJDK difere do código do HotSpot. O OpenJDK não utiliza essa variável na geração posterior de números aleatórios.

Artigos relacionados

Explorar mais ofertas especiais

  1. Short Message Service (SMS) e Serviço de e-mail

    Pacote de 50.000 e-mails a partir de apenas US$ 1,99; 120 mensagens curtas a partir de apenas US$ 1,00

phone Fale Conosco
Oi, eu sou o assistente de IA da Alibaba Cloud!
Posso ajudar com perguntas e soluções.