Compartilhar via


Conteinerizar seus aplicativos Java

Este artigo fornece uma visão geral das estratégias e configurações recomendadas para o contêiner de aplicativos Java. Ao colocar em contêiner um aplicativo Java, considere cuidadosamente quanto tempo de CPU o contêiner tem disponível. Em seguida, considere a quantidade de memória disponível em termos de quantidade total de memória e o tamanho do heap da JVM (Máquina Virtual Java). Em ambientes em contêineres, os aplicativos podem ter acesso a todos os processadores e, portanto, poder executar vários threads em paralelo. No entanto, é comum que os contêineres tenham uma cota de CPU aplicada que possa limitar o acesso a CPUs.

A JVM tem heurística para determinar o número de "processadores disponíveis" com base na cota de CPU, o que pode influenciar drasticamente o desempenho de aplicativos Java. A memória alocada para o próprio contêiner e o tamanho da área de heap para a JVM são tão importantes quanto os processadores. Esses fatores determinam o comportamento do coletor de lixo (GC) e o desempenho geral do sistema.

Colocar em contêiner um novo aplicativo

Ao colocar em contêiner uma carga de trabalho Java para um novo aplicativo, você deve levar duas coisas em conta ao pensar em memória:

  • A memória alocada para o próprio contêiner.
  • A quantidade de memória disponível para o processo java.

Entender a ergonomia padrão da JVM

Os aplicativos precisam de um ponto de partida e configurações. A JVM tem ergonomia padrão com valores predefinidos baseados no número de processadores disponíveis e na quantidade de memória no sistema. Os valores padrão mostrados nas tabelas a seguir são usados quando a JVM é iniciada sem sinalizadores ou parâmetros de inicialização específicos.

A tabela a seguir mostra o GC padrão usado para os recursos disponíveis:

Recursos disponíveis GC padrão
Qualquer número de processadores
Até 1.791 MB de memória
SerialGC
Mais de 2 processadores
1.792 MB ou mais de memória
G1GC

A tabela a seguir mostra o tamanho máximo do heap padrão, dependendo da quantidade de memória disponível no ambiente em que a JVM está em execução:

Memória disponível Tamanho máximo de heap padrão
Até 256 MB 50% de memória disponível
256 MB a 512 MB ~127 MB
Mais de 512 MB 25% de memória disponível

O tamanho do heap inicial padrão é 1/64 de memória disponível. Esses valores são válidos para o OpenJDK 11 e posterior e para a maioria das distribuições, incluindo o Microsoft Build do OpenJDK, Azul Zulu, Eclipse Temurin, Oracle OpenJDK e outros.

Determinar a memória do contêiner

Escolha um valor de memória de contêiner que atenda melhor à carga de trabalho, dependendo das necessidades do aplicativo e dos padrões de uso distintos. Por exemplo, se o aplicativo criar grafos de objeto grandes, você provavelmente precisará de mais memória do que precisaria para aplicativos com muitos grafos de objeto pequenos.

Dica

Se você não souber quanta memória alocar, um bom ponto de partida será 4 GB.

Determinar a memória do heap JVM

Quando você aloca memória de heap da JVM, a JVM precisa de mais memória do que a que é usada para o heap da JVM. Quando você define a memória máxima do heap de JVM, ela nunca deve ser igual à quantidade de memória do contêiner, pois isso causa erros de contêiner sem memória (OOM) e falhas de contêiner.

Dica

Aloque 75% de memória de contêiner para o heap JVM.

No OpenJDK 11 e posterior, você pode definir o tamanho do heap JVM das seguintes maneiras:

Descrição Bandeira Exemplos
Valor fixo -Xmx -Xmx4g
Valor dinâmico -XX:MaxRAMPercentage -XX:MaxRAMPercentage=75

Tamanho mínimo/inicial do heap

Quando o ambiente tem a garantia de ter uma determinada quantidade de memória reservada para uma instância de JVM, como em um contêiner, você deve definir o tamanho mínimo do heap - ou o tamanho inicial do heap - para o mesmo tamanho do tamanho máximo do heap. Essa configuração indica à JVM que ela não deve executar a tarefa de liberar memória para o sistema operacional.

Para definir um tamanho mínimo de heap, use -Xms para valores absolutos ou -XX:InitialRAMPercentage para quantidades percentuais.

Importante

O sinalizador -XX:MinRAMPercentage, apesar do que o nome sugere, é usado para definir o padrão percentual máximo de RAM para sistemas com até 256 MB de RAM disponíveis no sistema.

Gráfico mostrando o tamanho do heap padrão no OpenJDK 17.

Determinar qual GC será usado

Anteriormente, você determinou a quantidade de memória de heap JVM com a qual começar. A próxima etapa será escolher seu GC. A quantidade máxima de memória de heap JVM disponível é frequentemente um fator na escolha do seu GC. A tabela a seguir descreve as características de cada GC.

Fatores SerialGC ParallelGC G1GC ZGC ShenandoahGC
Número de núcleos 1 2 2 2 2
Vários threads Não Sim Sim Sim Sim
Tamanho do heap Java <4 GBytes <4 GBytes >4 GBytes >4 GBytes >4 GBytes
Pausa Sim Sim Sim Sim (<1 ms) Sim (<10 ms)
Custos gerais indiretos Mínimo Mínimo Moderado Moderado Moderado
Efeito de latência final Alto Alto Alto Baixo Moderado
Versão do JDK Todos Tudo JDK 8+ JDK 17+ JDK 11+
Mais adequado para Pequenos heaps de núcleo único Pequenos heaps de vários núcleos ou cargas de trabalho em lotes com qualquer tamanho de heap Responsiva com grandes volumes de dados (interações solicitação-resposta/banco de dados) Responsiva com grandes volumes de dados (interações solicitação-resposta/banco de dados) Responsiva com grandes volumes de dados (interações solicitação-resposta/banco de dados)

Dica

Para a maioria dos aplicativos de microsserviço de uso geral, comece com o GC Paralelo.

Determinar quantos núcleos de CPU são necessários

Para qualquer GC diferente do SerialGC, recomendamos dois ou mais núcleos de vCPU – ou pelo menos 2000m para cpu_limit em Kubernetes. Não recomendamos selecionar nada menos que um núcleo de vCPU em ambientes em contêineres.

Dica

Se você não souber com quantos núcleos começar, uma boa opção será dois núcleos de vCPU.

Escolher um ponto de partida

Recomendamos começar com duas réplicas ou instâncias em ambientes de orquestração de contêineres, como Kubernetes, OpenShift, Azure Spring Apps, Aplicativos de Contêiner do Azure e Serviço de Aplicativo do Azure. A tabela a seguir resume os pontos de partida recomendados para a contêinerização do novo aplicativo Java.

Núcleos de vCPU Memória do contêiner Tamanho do heap de JVM GC Réplicas
2 4 GB 75% ParallelGC 2

Use os seguintes parâmetros JVM:

-XX:+UseParallelGC -XX:MaxRAMPercentage=75

Colocar em contêiner um aplicativo local existente

Se o aplicativo já estiver em execução no local ou em uma VM na nuvem, recomendamos que você comece com a seguinte configuração:

  • A mesma quantidade de memória à qual o aplicativo tem acesso no momento.
  • O mesmo número de CPUs ou núcleos de vCPU que o aplicativo tem atualmente disponível.
  • Os mesmos parâmetros JVM que você usa atualmente.

Se os núcleos de vCPU ou a combinação de memória de contêiner não estiverem disponíveis, escolha a opção mais próxima, arredondando para cima tanto os núcleos de vCPU quanto a memória do contêiner.

Próximas etapas

Agora que você entende as recomendações gerais para contêineres de aplicativos Java, prossiga para o seguinte artigo para estabelecer uma linha de base de contêinerização: