Olá a todos! Este é o segundo post da nossa série sobre Quarkus – hoje vamos falar sobre a compilação nativa.

– é um stack Java projetado para . E embora haja muito a ser feito, trabalhamos bem em vários aspectos, incluindo a otimização da JVM e uma série de frameworks. Uma das características do Quarkus que atraiu a atenção dos desenvolvedores é a abordagem integrada e contínua para transformar código Java em arquivos executáveis para sistemas operacionais específicos (o chamado 'compilação nativa'), semelhante ao que ocorre em C e C++, onde tal compilação geralmente acontece no final de um ciclo de construção, teste e implantação.
E embora a compilação nativa, como mostraremos abaixo, seja importante, é preciso notar que o Quarkus realmente funciona muito bem também em uma simples máquina Java, OpenJDK Hotspot, graças às melhorias de desempenho que implementamos em toda a stack. Portanto, a compilação nativa deve ser vista como um bônus adicional que pode ser utilizado conforme desejado ou necessário. Na verdade, no que diz respeito às imagens nativas, o Quarkus depende consideravelmente do OpenJDK. E o modo dev, muito bem recebido pelos desenvolvedores, oferece testes quase instantâneos de mudanças devido às capacidades avançadas de execução dinâmica de código implementadas no Hotspot. Além disso, ao criar imagens nativas, o GraalVM utiliza a biblioteca de classes do OpenJDK e as capacidades do HotSpot.
Então, por que a compilação nativa é necessária se tudo já está tão bem otimizado? Vamos tentar responder a essa pergunta abaixo.
Comecemos pelo óbvio: a Red Hat tem uma ampla experiência em otimização de JVM, stacks e frameworks ao longo do desenvolvimento do projeto , incluindo:
- O primeiro servidor de aplicações para trabalhar na nuvem na plataforma .
- O primeiro servidor de aplicações para funcionar em computadores .
- O primeiro servidor de aplicações para funcionar em .
- Uma série de projetos operando em dispositivos .
Estamos há muitos anos lidando com problemas de execução de aplicações Java na nuvem e em dispositivos com recursos limitados (ou seja, IoT) e aprendemos a extrair o máximo da JVM em termos de desempenho e otimização de memória. Assim como muitos outros, já trabalhamos com a compilação nativa de aplicações Java através de , , e até mesmo E entendemos bem os prós e contras dessa abordagem (por exemplo, o dilema entre a versatilidade do 'build once – run anywhere' e o fato de que aplicativos compilados têm um tamanho menor e iniciam mais rapidamente).
Por que é tão importante considerar esses prós e contras? Porque, em algumas situações, a relação entre eles se torna decisiva:
- Por exemplo, em ambientes serverless/event-driven, onde em modo (duro ou leve) tempo real, para conseguir reagir aos eventos. Ao contrário de serviços persistentes de longa duração, aqui a duração do início a frio aumenta criticamente o tempo de resposta à solicitação. O tempo de inicialização da JVM ainda consome um tempo considerável, e, embora em alguns casos isso possa ser reduzido por métodos puramente de hardware, a diferença entre um segundo e 5 milissegundos pode ser uma questão de vida ou morte. Sim, aqui é possível brincar com a criação de uma reserva quente de máquinas Java (o que, por exemplo, fizemos ao ), mas isso por si só não garante um número suficiente de JVMs para lidar com as solicitações à medida que a carga aumenta. E do ponto de vista econômico, certamente não é a opção mais adequada.
- Além disso, há outro aspecto frequentemente mencionado, que é a multitenancy. Apesar de a JVM ter se aproximado bastante das capacidades dos sistemas operacionais, elas ainda não conseguem fazer o que estamos acostumados a ver no Linux – isolar processos. Portanto, a falha de uma thread pode derrubar toda a máquina Java. Muitos tentam contornar essa limitação designando uma JVM separada para os aplicativos de cada usuário, a fim de minimizar as consequências de falhas. Isso faz bastante sentido, mas não combina bem com a escalabilidade.
- Além disso, para aplicativos orientados à nuvem, um indicador importante é a densidade de serviços no host. A transição para a metodologia , microsserviços e Kubernetes aumentam o número de instâncias Java por aplicativo. Ou seja, por um lado, tudo isso oferece elasticidade e confiabilidade, mas, ao mesmo tempo, o consumo de memória base por serviço também cresce, e parte desse consumo nem sempre é estritamente necessária. Arquivos executáveis compilados estaticamente se destacam aqui por meio de várias técnicas de otimização, como a eliminação de código morto de baixo nível, onde apenas as partes dos frameworks (incluindo o próprio JDK) que o serviço realmente utiliza são incluídas na imagem final. Portanto, a compilação nativa do Quarkus ajuda a alocar instâncias de serviços mais densamente no host sem comprometer a segurança.
Na verdade, os argumentos apresentados acima já são suficientes para entender a justificativa da compilação nativa do ponto de vista dos participantes do projeto Quarkus. No entanto, há ainda outra razão, não técnica, mas igualmente importante: nos últimos anos, muitos programadores e empresas desenvolvedoras abandonaram o Java em favor de novas linguagens de programação, considerando que o Java, juntamente com suas JVMs, pilhas e frameworks, se tornou excessivamente exigente em termos de memória, muito lento etc.
No entanto, o hábito de usar a mesma ferramenta para resolver qualquer tarefa – . Às vezes, é melhor dar um passo atrás e procurar algo diferente. E se o Quarkus leva as pessoas a fazer uma pausa e refletir, isso é bom para todo o ecossistema Java. O Quarkus representa uma visão inovadora sobre como criar aplicações mais eficientes, tornando o Java mais relevante para novas arquiteturas de aplicações, como serverless. Além disso, devido à sua extensibilidade, o Quarkus, esperamos, terá todo um ecossistema de extensões Java, aumentando significativamente o número de frameworks que, por padrão, suportarão a compilação nativa dentro das aplicações.
Fonte: habr.com
