"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Gerenciamento de memória na imagem nativa GraalVM

Gerenciamento de memória na imagem nativa GraalVM

Publicado em 2024-11-07
Navegar:496

O gerenciamento de memória é um componente crucial do desenvolvimento de software de computador, encarregado da alocação, utilização e liberação eficazes de memória em aplicativos. Sua importância reside em melhorar o desempenho do software e garantir a estabilidade do sistema.

Coleta de lixo

A coleta de lixo (GC) é fundamental nas linguagens de programação contemporâneas, como Java e Go. Ele detecta e recicla de forma autônoma a memória não utilizada, aliviando assim a necessidade dos desenvolvedores gerenciarem manualmente a memória. O conceito de GC surgiu originalmente na linguagem de programação LISP no final da década de 1950, marcando a introdução do gerenciamento automatizado de memória.

As principais vantagens do gerenciamento automatizado de memória incluem:

  • Prevenção de vazamentos de memória e utilização eficiente da memória.
  • Processos de desenvolvimento simplificados e maior estabilidade do programa.

Compreender a natureza do “lixo” na memória e identificar o espaço recuperável é essencial. Nos próximos capítulos, começaremos explorando os princípios fundamentais da coleta de lixo.

Algoritmo de contagem de referência [George E. Collins 1966]

O algoritmo de contagem de referências atribui um campo no cabeçalho do objeto para rastrear sua contagem de referências. Essa contagem aumenta a cada nova referência e diminui quando uma referência é removida. Quando a contagem chega a zero, o objeto está qualificado para coleta de lixo.

Considere o seguinte código:

Primeiro crie uma String com valor demo que é referenciada por d (Figura 1).

String d = new String("demo");

Memory Management in GraalVM Native Image

Figura 1 – Após a criação de uma String

Então, defina d como nulo. A contagem de referência da demonstração é zero. No algoritmo de contagem de referência, a memória para demonstração deve ser recuperada (Figura 2).

d =null; // Reference count of 'demo' becomes zero, prompting garbage collection.

Memory Management in GraalVM Native Image

Figura 2 – Quando a referência é anulada

O algoritmo de contagem de referências opera durante a execução do programa, evitando eventos Stop-The-World, que interrompem o programa temporariamente para coleta de lixo. No entanto, sua principal desvantagem é a incapacidade de lidar com referências circulares (Figura 3).

Por exemplo:

public class CircularReferenceDemo {

  public CircularReferenceDemo reference;
  private String name;

  public CircularReferenceDemo(String name) {
    this.name = name;
  }

  public void setReference(CircularReferenceDemo ref) {
    this.reference = ref;
  }

  public static void main(String[] args) {
    CircularReferenceDemo objA = new CircularReferenceDemo("Ref_A");
    CircularReferenceDemo objB = new CircularReferenceDemo("Ref_B");

    objA.setReference(objB);
    objB.setReference(objA);

    objA = null;
    objB = null;
  }
}

Aqui, apesar de anularem referências externas, as referências mútuas entre objA e objB impedem sua coleta de lixo.

Memory Management in GraalVM Native Image

Figura 3 – Referências Circulares

Podemos ver que ambos os objetos não podem mais ser acessados. No entanto, eles são referenciados entre si e, portanto, sua contagem de referências nunca será zero. Conseqüentemente, o coletor GC nunca será notificado para coletar o lixo usando o algoritmo Reference Counting.

Este algoritmo é praticamente implementado em C através do uso de std::shared_ptr. Projetado para gerenciar o ciclo de vida de objetos alocados dinamicamente, std::shared_ptr automatiza o incremento e decremento de contagens de referência à medida que ponteiros para o objeto são criados ou destruídos. Este ponteiro inteligente faz parte da Biblioteca Padrão C, fornecendo recursos robustos de gerenciamento de memória que diminuem significativamente os riscos associados ao manuseio manual de memória. Sempre que um std::shared_ptr é copiado, a contagem de referência interna do objeto gerenciado aumenta, refletindo a nova referência. Por outro lado, quando um std::shared_ptr é destruído, sai do escopo ou é reatribuído a um objeto diferente, a contagem de referências diminui. A memória alocada é recuperada automaticamente e o objeto é destruído quando sua contagem de referência chega a zero,
evitando efetivamente vazamentos de memória, garantindo que nenhum objeto permaneça alocado sem necessidade.

Algoritmo de Análise de Acessibilidade [1978]

O algoritmo de análise de acessibilidade começa nas raízes do GC, percorrendo o gráfico do objeto. Objetos que não podem ser alcançados a partir dessas raízes são considerados irrecuperáveis ​​e são direcionados para coleta.

Conforme mostrado na imagem abaixo, os objetos no círculo azul devem ser mantidos vivos e os objetos no círculo cinza podem ser reciclados (Figura 4).

Memory Management in GraalVM Native Image

Figura 4 – Vazamento de memória

Este método resolve efetivamente o problema de referências circulares inerentes ao algoritmo de contagem de referências. Objetos inacessíveis a partir das raízes do GC são categorizados para coleta.

Normalmente, os objetos Java considerados como raízes de GC incluem:

  • Variáveis ​​locais dentro do escopo do método atual.
  • Tópicos Java ativos.
  • Campos estáticos das classes.
  • Referências JNI do código nativo.

Visão geral da imagem nativa do GraalVM

GraalVM oferece um compilador antecipado (AOT), que traduz aplicativos Java em binários executáveis ​​independentes conhecidos como GraalVM Native Images. Desenvolvidos pela Oracle Labs, esses binários
encapsula classes de aplicativos e bibliotecas e componentes de tempo de execução como o GC, permitindo operações sem um Java Runtime Environment (JRE).

O processo envolve análise estática para determinar componentes acessíveis, inicialização por meio de blocos executados e finalização criando um instantâneo do estado do aplicativo para tradução subsequente do código de máquina.

Fundamentos da VM do substrato

O Substrate VM é parte integrante do conjunto GraalVM, orquestrado pelo Oracle Labs. É uma JVM aprimorada que não apenas oferece suporte à compilação antecipada (AOT), mas também facilita a execução de linguagens além do Java, como JavaScript, Python, Ruby e até mesmo linguagens nativas como C e C. Basicamente, o Substrate VM serve como uma estrutura sofisticada que permite ao GraalVM compilar aplicativos Java em binários nativos independentes. Esses binários não dependem de uma Java Virtual Machine (JVM) convencional para sua execução, o que agiliza a implantação e
processos operacionais.

Um dos principais recursos do Substrate VM é seu coletor de lixo especializado, que é ajustado para aplicativos que exigem baixa latência e consumo mínimo de memória. Esse coletor de lixo é adepto de lidar com o layout de memória exclusivo e o modelo operacional distinto das imagens nativas, que diferem consideravelmente dos aplicativos Java tradicionais executados em uma JVM padrão. A ausência de um compilador Just-In-Time (JIT) nas imagens nativas do Substrate VM é uma escolha estratégica que ajuda a minimizar o tamanho geral do executável. Isso ocorre porque elimina a necessidade de incluir o compilador JIT e os metadados associados, que são substanciais em tamanho e complexidade.

Além disso, embora GraalVM seja desenvolvido usando Java, isso introduz certas restrições, particularmente em termos de acesso à memória nativa. Essas restrições devem-se principalmente a questões de segurança e à necessidade de manter a compatibilidade entre várias plataformas. Entretanto, acessar a memória nativa é essencial para operações ideais de coleta de lixo. Para resolver isso, o Substrate VM emprega um conjunto de interfaces especializadas que facilitam interações seguras e eficientes com a memória nativa. Essas interfaces fazem parte da arquitetura GraalVM mais ampla e permitem que o Substrate VM gerencie efetivamente a memória de maneira semelhante a linguagens de nível inferior como C, ao mesmo tempo que mantém a segurança e a capacidade de gerenciamento do Java.

Na prática, esses recursos tornam o Substrate VM uma ferramenta extremamente versátil que aprimora a funcionalidade e a eficiência dos aplicativos compilados com GraalVM. Ao permitir que os desenvolvedores
aproveitando uma gama mais ampla de linguagens de programação e compilando-as em binários nativos eficientes, o Substrate VM amplia os limites do que pode ser alcançado com ambientes de desenvolvimento Java tradicionais. Isso o torna um recurso inestimável para projetos modernos de desenvolvimento de software que exigem alto desempenho, consumo reduzido de recursos e suporte versátil a idiomas.

Elementos dignos de nota do Substrate VM incluem:

  • Acesso simplificado à memória por meio de interfaces como Pointer Interface Pointer para operações de memória bruta e WordBase Interface WordBase para lidar com valores do tamanho de palavras.

  • Divisão do heap em segmentos pré-inicializados contendo objetos imutáveis ​​e segmentos de tempo de execução para alocação dinâmica de objetos (Figura 5).

Memory Management in GraalVM Native Image

Figura 5 – Gerenciamento de memória em imagem nativa

Em tempo de execução, o chamado heap de imagem no Substrate VM contém objetos criados durante o processo de construção da imagem. Esta seção do heap é pré-inicializada com dados da seção de dados do binário executável e está prontamente acessível na inicialização do aplicativo. Os objetos residentes na pilha de imagens são considerados imortais; portanto, as referências dentro desses objetos são tratadas como ponteiros raiz pelo
coletor de lixo. No entanto, o GC verifica apenas partes do heap de imagem em busca de ponteiros raiz, especificamente aqueles que não estão marcados como somente leitura.

Durante o processo de construção, os objetos designados como somente leitura são colocados em uma seção somente leitura específica do heap de imagem. Como esses objetos nunca manterão referências a objetos alocados em tempo de execução, eles não contêm ponteiros raiz, permitindo que o GC os ignore durante as varreduras. Da mesma forma, objetos que consistem apenas em dados primitivos ou matrizes de tipos primitivos também carecem de ponteiros raiz. Este atributo agiliza ainda mais o processo de coleta de lixo, pois esses objetos podem ser omitidos nas verificações do GC.

Em contraste, o heap Java é designado para armazenar objetos comuns que são criados dinamicamente durante o tempo de execução. Esta parte do heap está sujeita à coleta regular de lixo para recuperar o espaço ocupado por objetos que não estão mais em uso. Está estruturado como um conjunto geracional com mecanismos de envelhecimento, facilitando o gerenciamento eficiente da memória ao longo do tempo.

Essa divisão entre o heap de imagem imortal pré-inicializado e o heap Java gerenciado dinamicamente permite que o Substrate VM otimize o uso de memória e a eficiência da coleta de lixo, atendendo a aspectos estáticos e dinâmicos dos requisitos de memória do aplicativo.

Pedaço de pilha

No modelo heap do Substrate VM, a memória é sistematicamente organizada em estruturas conhecidas como heap chunks. Esses pedaços, normalmente dimensionados em 1.024 KB por padrão, formam um segmento contínuo de memória virtual que é alocado exclusivamente para armazenamento de objetos. A estrutura organizacional desses pedaços é uma lista vinculada onde o pedaço final representa o segmento adicionado mais recentemente. Esse modelo
facilita a alocação eficiente de memória e o gerenciamento de objetos.

Esses pedaços de heap são categorizados em dois tipos: alinhados e não alinhados. Pedaços de heap alinhados são capazes de conter vários objetos continuamente. Este alinhamento permite um mapeamento mais simples de
objetos em seus respectivos pedaços de heap pai, tornando o gerenciamento de memória mais intuitivo e eficiente. Em cenários onde a promoção de objetos é necessária - normalmente, durante a coleta de lixo e
otimização de memória - um objeto é movido de seu posicionamento original em um pedaço de heap pai para um pedaço de heap de destino localizado em um "espaço antigo" designado. Essa migração faz parte da estratégia de gerenciamento de heap geracional que ajuda a otimizar o processo de coleta de lixo, separando objetos novos de antigos, reduzindo assim a sobrecarga durante os ciclos de GC.

Coletores de lixo em imagem nativa

GraalVM Native Image suporta vários GCs adaptados a diferentes necessidades:

  • Serial GC: Coletor padrão de baixo impacto adequado para aplicações de thread único.

  • G1 Garbage Collector: Projetado para aplicativos multithread com grandes tamanhos de heap, aumentando a flexibilidade no gerenciamento de geração.

  • Epsilon GC: Um coletor minimalista que lida com alocação, mas não tem recuperação, melhor usado para aplicativos de curta duração onde a utilização total do heap é previsível.

Conclusão

Concluindo, o Substrate VM otimiza efetivamente o gerenciamento de memória dentro do GraalVM, incorporando técnicas avançadas como coleta de lixo especializada e gerenciamento estruturado de heap. Esses recursos, incluindo pedaços de heap e segmentos de memória separados para heaps de imagem e Java, simplificam a coleta de lixo e melhoram o desempenho do aplicativo. Como o Substrate VM suporta uma variedade de linguagens de programação e as compila em binários nativos eficientes, ele mostra como as estruturas JVM modernas podem se estender além dos limites tradicionais para aumentar a eficiência e a robustez da execução em diversos ambientes de aplicativos. Essa abordagem estabelece um alto padrão para desenvolvimentos futuros em tecnologia de máquinas virtuais e implantação de aplicativos.

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/yanev/memory-management-in-graalvm-native-image-4nbe?1 Se houver alguma violação, entre em contato com [email protected] para excluí-la
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3