Ajuste de desempenho Java: 10 técnicas comprovadas para maximizar a velocidade Java

Ajuste de desempenho Java: 10 técnicas comprovadas para maximizar a velocidade Java

Melhore o desempenho de seus aplicativos Java com técnicas de ajuste eficazes. Descubra estratégias para otimizar seu código e aumentar a eficiência geral.

Imagem em destaque

Uma das linguagens de programação mais populares com uma ampla variedade de usos, Java possui muitas qualidades úteis. No entanto, tem sido frequentemente criticado por seu desempenho.

Como você resolve problemas de desempenho Java em seus serviços de desenvolvimento Java? Aqui, exploraremos diversas técnicas para otimizar o desempenho de seus aplicativos, um processo conhecido como ajuste de desempenho Java.

Perfil nº 1 para degradação de desempenho

Ao otimizar seu código, você deve tentar encontrar os maiores problemas de desempenho e corrigi-los primeiro. Mas às vezes não é óbvio qual parte está causando os problemas. Nestes casos, é melhor usar um perfilador.

Um criador de perfil é uma ferramenta usada para identificar problemas de desempenho, como gargalos, vazamentos de memória e outras áreas de ineficiência no código.

Os criadores de perfil Java funcionam coletando dados sobre vários aspectos de um aplicativo Java em execução, como o tempo necessário para executar métodos, alocações de memória, comportamento de thread e uso de CPU. Esses dados são então analisados ​​para fornecer informações detalhadas sobre as características de desempenho do aplicativo.

Alguns criadores de perfil Java comumente usados ​​são VisualVM, JProfilere SeuKit. O Intellij também fornece um conjunto de ferramentas para criação de perfil de aplicativos.

Nº 2 Teste de desempenho

O teste de desempenho é o processo de submeter seu aplicativo a situações realistas ou extremas e analisar seu desempenho. Algumas opções populares são Apache JMeter, Gatlinge BlazeMeter.

Criação de perfil e teste de desempenho são duas coisas diferentes. Criar perfis é semelhante a observar um carro de perto e examinar suas diferentes partes. Por outro lado, o teste de desempenho é como dar uma volta em um carrinho de brinquedo e ver seu desempenho em diferentes situações. Depois de realizar testes de desempenho e identificar alguns erros, você pode usar uma ferramenta de criação de perfil para encontrar a causa subjacente desses problemas.

Nº 3 Teste de carga

O teste de carga é um tipo de teste de desempenho que envolve a simulação de cargas realistas em um sistema ou aplicativo para medir seu comportamento em condições normais e de pico de carga.

Imagine que você tem um site e quer saber quantas pessoas podem usá-lo ao mesmo tempo sem que ele quebre ou fique muito lento. Para isso, utilizamos ferramentas especiais que simulam um grande número de usuários utilizando o site ou aplicativo ao mesmo tempo.

Alguns aplicativos populares de teste de carga são Apache JMeter, WebLoadCarregarUI, LoadRunnerNeoLoad, LoadNinja e assim por diante.

Nº 4 Use a declaração preparada

Em Java, Statement é usado para executar consultas SQL. No entanto, a classe Statement tem algumas falhas. Vejamos o exemplo abaixo.

Connection db_con = DriverManager.getConnection ;
Statement st = db_con.createStatement ;

String username = "USER INPUT";
String query = "SELECT user FROM users WHERE username="" + username + """;
ResultSet result = st.executeQuery(query);

Se o usuário passasse ' OR 1=1– a consulta resultante seria:

SELECT user FROM users WHERE username="" OR 1=1 -- '

Isso faria com que a consulta retornasse todos os registros da tabela dos usuários, pois a condição OR 1=1 é sempre verdadeira. O traço duplo – no final é um comentário em SQL, que faz com que o restante da consulta original seja ignorado. Esse tipo de ataque é chamado de SQL Injection.

As instruções são vulneráveis ​​a injeções de SQL e, portanto, você deve usar PreparedStatement.

PreparedStatements são instruções SQL pré-compiladas. Sendo pré-compilados, eles têm muito melhor desempenho do que as instruções que são compiladas para cada nova consulta. Ao contrário das declarações, elas podem ser reutilizadas várias vezes com parâmetros diferentes. Você pode colocar “?” na consulta que pode ser substituída por um parâmetro desejado.

#5 Otimize Strings

Como sabemos, strings em Java são objetos. No entanto, você deve ter notado que as strings se comportam de maneira um pouco diferente dos objetos comuns. Aqui está um exemplo para demonstrar.

Quando criamos dois objetos, eles recebem locais de memória diferentes e uma comparação de igualdade retorna falso.

Object obj1 = new Object ;
Object obj2 = new Object ;
System.out.print(obj1 == obj2);  // false

Mas para strings, a comparação de igualdade em duas strings semelhantes resulta em verdade. Na verdade, isso ocorre apenas quando criamos strings através da sintaxe literal. Se você criar strings usando o construtor, a comparação de igualdade produzirá falso.

String str1 = "java";
String str2 = "java";
System.out.print(str1 == str2);  // true

String obj1 = new String("java");
String obj2 = new String("java");
System.out.print(str1 == str2);  // false

Isso ocorre porque quando strings são criadas usando a sintaxe literal, elas são armazenadas em um pool de strings, que faz parte da memória heap. Sempre que uma nova string é criada, Java verifica se existem strings semelhantes e, se existir, retorna a referência ao objeto string original em vez de criar um novo.

Outra coisa a notar é que as strings são imutáveis ​​e, portanto, os métodos de string retornam novas strings em vez de modificar as antigas. Portanto, toda vez que você modifica uma string, você está criando novas strings, cada uma das quais verifica se há duplicatas no pool de strings. Isso não está otimizado.

StringBuilder e StringBuffer

Para resolver esses problemas, Java fornece a classe StringBuilder, que é apenas uma sequência mutável de caracteres. Use StringBuilder ao modificar strings com frequência.

O StringBuffer funciona de forma muito semelhante ao StringBuilder, exceto que pode ser sincronizado em vários threads.

Apache Commons StringUtils

A classe StringUtils fornece alguns métodos adicionais para lidar com strings. Alguns deles têm melhor desempenho do que os métodos de string nativos. Por exemplo, StringUtils.replace é mais rápido que String.replace . Além disso, a maioria dos métodos são seguros para nulos, o que significa que eles não lançam NullPointerException se a string for nula.

Expressões regulares

Use expressões regulares ao combinar um padrão porque elas são muito mais eficientes do que qualquer técnica iterativa de correspondência de padrões.

Nº 6: Otimize o processo de coleta de lixo da máquina virtual Java

A JVM é responsável por gerenciar o ambiente de execução de aplicativos Java, incluindo gerenciamento de memória, gerenciamento de threads e processo de coleta de lixo. As configurações padrão da JVM são otimizadas para uma ampla variedade de aplicativos e configurações de hardware, mas podem não ser ideais para aplicativos e ambientes específicos.

Escolhendo o coletor de lixo certo

O coletor padrão para JDK 9 e superior é o coletor G1, que foi projetado para ser dimensionado de acordo com o tamanho da memória heap. O ZGC foi desenvolvido para aplicações com grandes pilhas que exigem baixo rendimento.

-XX:+UseG1GC – para usar o coletor G1

-XX:+UnlockExperimentalVMOptions -XX:+UseZGC – para usar ZGC

Ajustando o tamanho da memória heap

O heap é a área da memória onde os objetos são alocados. Ao ajustar o tamanho da memória heap, você pode otimizar o uso da memória e evitar problemas como OutOfMemoryError ou coleta de lixo excessiva.

Para definir o tamanho do heap, você pode usar os sinalizadores -Xmx e -Xms.

O sinalizador -Xmx limita o tamanho máximo de heap que a JVM pode alocar. Por exemplo, passar -Xmx2g limitará o tamanho máximo de heap a 2 gigabytes.

O sinalizador -Xms define o tamanho inicial do heap. Por exemplo, se você configurar -Xms256m, a JVM iniciará com um tamanho de heap de 256 megabytes.

Ajustando algumas opções do compilador

Existem muitos sinalizadores que podem ser usados ​​para personalizar o comportamento da Java Virtual Machine. Aqui estão alguns dos mais importantes:

-XX:MaxGCPauseMillis: esta opção especifica o tempo máximo que o coletor de lixo pode pausar o aplicativo.

-XX:UseAdaptiveSizePolicy: esta opção informa à JVM para ajustar dinamicamente o tamanho das gerações jovens e titulares com base no uso de memória do aplicativo.

#7 Use recursão

A recursão é uma ótima maneira de resolver problemas complexos onde a solução iterativa pode não ser óbvia. No entanto, você deve usar a recursão com moderação se o uso de memória for crítico (por exemplo, sistemas embarcados).

Para entender o porquê, vamos ver como a memória é alocada durante uma chamada de método.

Quando uma função é chamada, a JVM aloca um quadro de pilha para a função na pilha de chamadas, que contém as variáveis ​​locais e os argumentos do método da função. Se a função chamar outra função, um novo quadro de pilha será alocado para essa função e adicionado ao topo da pilha de chamadas.

Na abordagem iterativa, as variáveis ​​locais são criadas uma vez. Entretanto, no caso da abordagem recursiva, cada quadro de pilha possui seu próprio conjunto de variáveis ​​locais, que podem ocupar mais espaço do que o necessário.

Portanto, se você estiver trabalhando em um ambiente onde a memória é limitada, é melhor evitar a recursão ou adicionar algum tipo de verificação que impeça a recursão após um determinado limite.

#8 Uso de primitivos e wrappers

Primitivos são mais eficientes que suas classes wrapper. Isso é esperado porque os primitivos ocupam apenas uma quantidade fixa de espaço, enquanto as classes wrapper têm seus próprios métodos e variáveis ​​locais que ocupam algum espaço extra.

Por motivos semelhantes, tente evitar as classes BigInteger ou BigDecimal, se a precisão não for uma preocupação.

No entanto, há momentos em que você deve usar classes wrapper. Por exemplo, ao usar coleções como Listas e Mapas, a Java Virtual Machine converte as primitivas em suas respectivas classes wrapper (autoboxing). Nestes casos, o uso de primitivas pode levar a problemas de desempenho.

Ao criar instâncias de classe wrapper, tente usar o método estático valueOf em vez do construtor, pois ele está obsoleto desde Java 9.

#9 Use a versão mais recente do JDK

A menos que seu aplicativo dependa de alguns recursos de JDKs mais antigos que tenham compatibilidade retroativa limitada com os mais novos, não há muitos motivos para não usar os JDKs mais recentes. Cada nova versão vem com correções de bugs, melhorias de desempenho e patches de segurança. Versões mais recentes podem incluir recursos que melhoram seu código de várias maneiras.

#10 Otimização prematura

A otimização prematura refere-se à prática de otimização de código, incluindo o uso de estruturas Java antes que seja necessário. Não há nenhum benefício adicional em otimizar seu código ou selecionar estruturas Java específicas de antemão. É melhor se concentrar primeiro em fazer seu código funcionar e depois se preocupar em torná-lo mais rápido ou em selecionar as estruturas Java ideais.

Concentrar-se na otimização muito cedo no processo de desenvolvimento pode desviar tempo e recursos valiosos de tarefas mais críticas, como funcionalidade, confiabilidade e facilidade de manutenção.

Em vez de otimizar cada parte do código, otimize os componentes críticos ou gargalos que têm maior impacto no desempenho. Essa abordagem permite que você obtenha melhores resultados rapidamente.

Conclusão

Neste guia de ajuste de desempenho Java, exploramos métodos para aumentar a velocidade de seus aplicativos. Isso é crucial se você estiver trabalhando internamente ou terceirizando o desenvolvimento Java. Ao aderir a essas práticas recomendadas e compreender os recursos exatos de desempenho, você poderá lançar produtos de maior qualidade, independentemente da sua abordagem de desenvolvimento.

Perguntas frequentes

Como os desenvolvedores Java podem gerenciar com eficiência o uso de memória para minimizar o impacto no desempenho do aplicativo?

Para gerenciar a memória, os desenvolvedores Java precisam usar o coletor de lixo correto. O Coletor de Lixo G1 é o mais versátil. Segundo, você deve descartar objetos não utilizados definindo sua referência como nula.

Quais são algumas ferramentas e bibliotecas populares de otimização de desempenho Java para ajudar os desenvolvedores a melhorar o desempenho do aplicativo?

Algumas ferramentas populares para monitoramento de desempenho Java são JMeter, VisualVM, JProfiler e YourKit.

Além disso, Apache Commons são bibliotecas que fornecem classes de utilitários de maior desempenho para operações comuns.

Se você gostou disso, não deixe de conferir um de nossos outros artigos sobre Java:

  • 7 melhores bibliotecas de aprendizado de máquina Java
  • C# x Java: principais diferenças explicadas
  • Simultaneidade Java: domine a arte do multithreading
  • Coleta de lixo Java
  • Teste de integração Java explicado com exemplos

Fonte: BairesDev

Retour au blog

Laisser un commentaire

Veuillez noter que les commentaires doivent être approuvés avant d'être publiés.