Logo

dev-resources.site

for different kinds of informations.

Profiling no Java: Guia prático para analisar o desempenho de aplicações Java

Published at
12/21/2024
Categories
java
profiling
cpu
memory
Author
ifelipedev
Categories
4 categories in total
java
open
profiling
open
cpu
open
memory
open
Author
10 person written this
ifelipedev
open
Profiling no Java: Guia prático para analisar o desempenho de aplicações Java

Introdução

Quando aplicações são executadas, elas geram uma quantidade enorme de informações que na maioria das vezes são ignoradas, e quando essas aplicações enfrentam situações desagradáveis, como lentidão, alto consumo de memória, alta demanda de poder de processamento e outras coisas, essas informações passam a ser relevantes para que seja possível tornar o uso da aplicação viável operacionalmente e financeiramente. Uma forma de entender os problemas citados, é a partir do profiling.

Em um nível mais fundamental, Patel e Rajwat (2013) destacam que para manter um software eficiente, é essencial conhecer as necessidades de integrações, hardware e rede que um software demanda, esse conhecimento é necessário para avaliar qual o tipo de alteração e será necessária para cumprir com os requisitos do negócio, tendo este conhecimento, também é possível chegar a mais ideal das várias alternativas de profiling.

Se tratando especificamente do profiling de software, Obregon (2023) diz que o profiling é uma maneira dinâmica de realizar a análise de aplicações, é o processo de monitorar o comportamento de aplicações em tempo de execução, o que envolve coletar dados sobre performance, alocação de memória, uso de threads e outras métricas críticas. Esses dados fornecem ideias sobre como a aplicação está sendo executada e ajuda desenvolvedores a identificar aspectos ineficientes e potenciais pontos de melhoria.

Um profiling de software abrange diferentes componentes que uma aplicação utiliza, os principais são:
• CPU: revela os pontos que mais demandam recursos de processamento, permitindo identificar pontos de melhoria;
• Memória: possibilita identificar memory leak e entender como a memória é alocada e utilizada;
• Thread: em cenários onde multithread é normal, os desenvolvedores precisam entender o comportamento e o ciclo de vida das threads, visando evitar erros de sincronização, deadlocks e demais possíveis problemas.

No Java, também é possível considerar a análise do garbage collector (GC) como sendo um ponto de aplicar o profiling, visto que o GC é utilizado como gerenciador de memória de aplicações Java.

De maneira geral, pode-se dizer que a definição de profiling se consiste em um processo contínuo de coleta de dados de aplicações, que, quando realizada de maneira adequada, gera informações importantes para otimizar sistemas, aumentar o retorno financeiro e melhorar a satisfação de clientes.

Melhores práticas para o profiling de software

O profiling normalmente surge em cenários ruins para aplicações, portanto, antes de mudar qualquer comportamento, é apropriado fazer um snapshot sobre o funcionamento do sistema a ser analisado, de forma que que existam dados o suficiente para comparação após uma eventual correção ou melhoria. Ademais, é importante abordar o profiling de forma estruturada. Isso inclui: ideias claras sobre os problemas a serem resolvidos, coletas dos dados comparativos adequados (antes e depois de uma eventual melhoria) e interpretação clara sobre o resultado das comparações entre os dados coletados.

Chen (2024) e Obregon (2023) sugerem que o profiling deve ser empregado no cotidiano do fluxo de trabalho dos desenvolvedores, não apenas quando algo não vai bem. Eles argumentam que ajuda a captar melhorias possíveis de forma proativa no fluxo de desenvolvimento. No entanto, também destacam que é necessário focar principalmente na resolução dos gargalos essenciais, que resultarão em impactos mais significantes no desempenho e uso das aplicações.

Escolher as ferramentas certas também é essencial para conduzir um profiling adequadamente, existem diversas ferramentas que possibilitam o profiling de aplicações Java, algumas dessas ferramentas são: VisualVM, JConsole, JProfiler, IntelliJ IDEA Profiler e outros. A escolha da ferramenta dependerá da necessidade e complexidade da aplicação a ser analisada.

Principais componentes e técnicas de profiling no Java

O profiling de um software pode ter como alvo diferentes componentes que o software utiliza, portanto, existem técnicas adequadas para cada componente.

Profiling de CPU

É necessário entender como o CPU está sendo utilizado pela aplicação, isso envolve identificar métodos e gargalos que consomem muito tempo de processamento. Neste profiling, o ideal é buscar por métodos com muitas invocações ou com grandes tempos de execução.

• Técnica de amostragem: envolve coletar snapshots temporários da call stack (ou pilha de execução) e agregar as informações coletadas para identificar “hot spots” no código. É uma técnica leve para a aplicação e simples de se aplicar, ideal para avaliar cenários em ambiente produtivo, contudo, é menos preciso e pode omitir pequenas execuções de métodos.
• Técnica de instrumentalização: é uma técnica que modifica o bytecode da aplicação para incluir registros de tempo no momento da entrada e saída de processamento de métodos. Concede detalhes mais precisos do que a técnica anterior, mas, sobrecarrega a aplicação, podendo ocasionar em lentidões inesperadas.

Profiling de Memória

O profiling de memória é importante para identificar possíveis vazamentos de memória, e entender como a aplicação aloca e utiliza a memória. Neste profiling, é almejado encontrar padrões indesejados de uso de memória.

• Análise de heap: envolve verificar os objetos presentes na heap para identificar vazamentos e consumidores de muita memória.
• Análise de GC: ajuda a entender como a limpeza efetuada pelo GC atua e afeta a performance da aplicação.

Profiling de Threads

Técnicas de profiling de threads foca em identificar o que está acontecendo na thread no momento de execução, bem como providenciar dados sobre elas. Este profiling busca identificar o comportamento das threads, monitorar o estado e verificar problemas de sincronização ou deadlock delas.

• Monitoramento da JVM: envolve monitorar diversas métricas em nível da JVM, como uso da CPU e memória, contagem de threads e estatísticas do GC.
• Profiling diagnóstico: técnicas como uso do Java Flight Recorder permitem coletar dados detalhados de determinado tempo de execução sem grandes sobrecargas na aplicação, é útil para diagnosticar problemas em ambientes produtivos.

Estudo de caso aplicando profiling em aplicação Java

O profiling será conduzido com o VisualVM sobre uma aplicação que possui apenas uma API para gerar QRCode. Durante este profiling, serão apresentados dois pontos de melhoria fictícios, o primeiro, é sobre a velocidade de processamento do fluxo de gerar QRCode, e o segundo, sobre a otimização do consumo de memória heap deste mesmo fluxo.

As ferramentas e aplicações utilizadas durante este profiling serão: IntelliJ IDEA 2024.1.4, VisualVM 2.1.10, OpenJDK 21.0.1, Spring Boot 3.3.4, esta aplicação (payment-api), Apache Maven 3.9.6 e Postman v11.23.1 e Docker para levantar o banco de dados (Postgres) utilizado pela aplicação.

Após inicializar a aplicação, é possível inicializar também o VisualVM através de um plugin disponível para download no IntelliJ, o VisualVM Launcher.

Plugin VisualVM no IntelliJ.

O botão para inicializar o VisualVM no Intellij IDEA pode ser encontrado na barra lateral esquerda, no canto inferior, próximo ao console.

Botão para inicializar o VisualVM através do IntelliJ IDEA.

Após inicializar o VisualVM, será exibida a página inicial da aplicação e, ao lado esquerdo, um menu, que permite a visualização de todas as aplicações Java em execução no ambiente.

Tela inicial do VisualVM.

Após selecionar a aplicação “PaymentApiApplication”, será exibida a seguinte tela:

Tela exibida ao selecionar uma aplicação no VisualVM.

Esta tela apresenta algumas informações de execução da aplicação, o PID, host, classe principal, a JVM no qual a aplicação está rodando e outras coisas. Para iniciar o profiling, é necessário clicar em “Profiler”, informar os pacotes ou classes que devem ser monitorados em “CPU Settings” e em “Memory Settings”.

Configuração de pacotes e classes a serem monitorados.

Profiling de CPU

Após configurar os pacotes e classes a serem monitorados, é necessário clicar em “CPU” para inicializar o profiling através da técnica de instrumentalização do bytecode. Após clicar em ” CPU”, é necessário começar a gerar as requisições para a aplicação, de forma que gere insumos a serem analisados. Após inicializar o profiling e executar cinco requisições para a aplicação, todas as chamadas serão listadas pelo VisualVM, resultando na seguinte tela:

Tela do VisualVM após algumas requisições para a aplicação

Ao analisar as chamadas, é possível notar que existe um tempo muito grande de processamento no fluxo de geração do QRCode, principalmente na classe PaymentController, no método create:

Tela apresentando lentidão sobre a classe PaymentController.

Partindo da análise das informações nesta tela, é possível considerar que algo no método create não vai bem, e para este cenário, de fato não está bem, pois existe um Thread.sleep(3000) dentro dele:

Classe PaymentController, método create com lentidão de processamento.

Considerando o estado atual da aplicação, é ideal fazer um snapshot da versão “pré-correção” para que seja possível comparar com a versão da aplicação após a correção.

Botão para gerar snapshot.

Após a correção e reinicialização da aplicação, é necessário inicializar o profiling novamente. Ao fazer as mesmas requisições, o seguinte resultado é apresentado:

PaymentController no método create após a correção.

Nota-se que a aplicação obteve um ganho de três segundos sobre o tempo total de geração de QRCode, portanto, é possível verificar que o profiling de CPU através da instrumentalização do bytecode fornece informações valiosas sobre o tempo que o CPU passa em determinados locais da aplicação, podendo facilitar muito a análise e identificação de pontos de melhoria de código fonte em cenários produtivos.

Profiling de Memória

Para realizar o profiling sobre o uso de memória da aplicação será utilizada a técnica de análise de heap, e é necessário informar os pacotes a serem analisados em “Memory settings”, após isso, é preciso clicar em “Memory”, ao lado de “CPU”. Para este profiling, também serão realizadas cinco requisições para a API de geração de QRCode. Ao executar as cinco requisições, a seguinte tela será apresentada:

Tela de profiling de memória após cinco requisições.

Para este caso, não existem grandes otimizações a serem feitas, contudo, é possível notar que instâncias da classe PaymentDTOBuilder está tomando espaço na memória, mesmo não sendo necessário. Simulando uma pequena otimização e considerando que o ambiente depende dela, o ideal é realizar um snapshot após as requisições, remover o uso do builder do código e testar novamente. Após isso, o resultado alcançado será o seguinte:

Consumo de memória dos objetos instanciados pela aplicação após a correção.

Sem empregar o builder, é possível notar que houve uma redução no consumo de memória.

Além deste profiling, também é possível acompanhar métricas gerais da aplicação (como uso do CPU, heap, threads e classes carregadas) na aba “monitor”, como visto abaixo:

Monitor geral da aplicação no VisualVM.

Conclusão

Com base no estudo de caso e nos resultados obtidos, conclui-se que o uso de técnicas de profiling simplifica a identificação de pontos de melhoria em aplicações. Além de ser uma prática acessível, o uso adequado do profiling através das ferramentas apropriadas permite resolver inúmeros problemas produtivos antecipadamente e aumentar o grau de maturidade dos desenvolvedores que aplicam esta técnica regularmente. Ademais, promover o uso do profiling como parte do fluxo de desenvolvimento pode elevar a qualidade dos produtos desenvolvidos, melhorar a reputação de desenvolvedores e a consolidar a posição das empresas no mercado.

Referência bibliográfica

OBREGON, Alexander. Profiling Java Applications: Tools and Techniques. [S. l.], 10 nov. 2023. Disponível em: https://medium.com/@AlexanderObregon/profiling-java-applications-tools-and-techniques-3569b32862f4. Acesso em: 18 dez. 2024.

CHEN, Lily. Why profiling should be part of regular software development workflow. [S. l.], 17 mar. 2024. Disponível em: https://medium.com/performance-engineering-for-the-ordinary-barbie/why-profiling-should-be-part-of-regular-software-development-workflow-8b19b7f52b38. Acesso em: 18 dez. 2024.

PATEL, Rajendra; RAJWAT, Arvind. A Survey of Embedded Software Profiling Methodologies. [S. l.], 10 dez. 2013. Disponível em: https://arxiv.org/abs/1312.2949. Acesso em: 18 dez. 2024.

Links

Repositório do projeto utilizado: https://github.com/Zodh/payment-api

Imagem docker para o banco de dados utilizado: guilhermemmnn/fastfood-db:latest.

memory Article's
30 articles in total
Favicon
Memory Management in Operating Systems
Favicon
What is GCHandle in C#? (Part 1)
Favicon
How Memory Shapes Data Structures: Arrays and Allocation
Favicon
Mastering Pointers in Go: Enhancing Safety, Performance, and Code Maintainability
Favicon
Methods for finding memory leaks in Visual Studio
Favicon
Laravel 11: Allowed memory size of 134217728 bytes exhausted (tried to allocate 23085056 bytes)
Favicon
Setting up memory for Flink - Configuration
Favicon
CS50 - Week 4
Favicon
How to Create Dynamic Memory Card Game Using HTML CSS and JavaScript
Favicon
Profiling no Java: Guia prático para analisar o desempenho de aplicações Java
Favicon
Potential Drawbacks of Using DMA Instead of Interrupts for Data Transfer in Embedded Systems
Favicon
x64 Virtual Address Translation
Favicon
Why Is Stack Memory Faster Than Heap Memory? Here’s What You Need to Know!
Favicon
Java tool to accurately measure object sizes and their hierarchies.
Favicon
Physical and Logical Memory: Addressing and Allocation in Operating Systems
Favicon
Mastering memory management in Go: Avoiding slice-related leaks
Favicon
How to estimate Java object size
Favicon
The difference between pointers and values on methods
Favicon
Data Flow in LLM Applications: Building Reliable Context Management Systems
Favicon
JavaScript Shared Memory
Favicon
Understanding Memory<T> in C#
Favicon
The Power of Memory Map
Favicon
Node.js Memory Leaks: A Guide to Detection and Resolution
Favicon
Kubectl Top command:-Secrets behind scenes
Favicon
Subsistema de memória
Favicon
How to Increase Free Tier Memory on AWS EC2
Favicon
Understanding Memory Leaks in Java: Common Causes and How to Detect Them
Favicon
"What Every Programmer Should Know About Memory" by Ulrich Drepper.
Favicon
Understanding Garbage Collection in Java: Essential for Interview Preparation
Favicon
Navigating JVM Memory: Key Concepts for Your Java Interview

Featured ones: