Java Tunning

Rafael Hickmann
Sep 6, 2018 · 4 min read

Lembro de um dia ter tido um problema grave de performance em um projeto que participei, estávamos trabalhando com Java 7 utilizando Swing para criar uma aplicação que deveria conectar-se a um FTP e fazer envio de imagens direto para a conta do cliente que apareceriam no site principal. Quando lançamos o produto, depois de algumas horas sendo utilizado ele começava a ficar muito lento! Mobilizamos na época eu e mais um colega para tentar solucionar o problema de performance e depois de algumas boas horas tentando otimizar o Garbage Collector e os parâmetros da VM encontrei o erro, alguém tinha criado uma classe de logs utilizando String como objeto, se tivéssemos lido esse post isso teria nos poupado algumas boas horas de trabalho. Se você não entendeu qual o problema com o uso de String, acompanha o resto do post que você logo irá entender.

Concordo que hoje em dia falar de otimização de micro serviços nos remete a equações complicadas e horas ou dias de monitoramento e experimentação para chegar em algum resultado, em suma, isso pode até ser uma verdade mas acredite, muita coisa pode ser evitada apenas tendo alguns cuidados básicos na hora de desenvolver em Java.

Gostaria de deixar algumas dicas de otimização para desenvolvedores Java.

1 — Não otimize sem necessidade

Essa talvez seja uma das dicas mais valiosas que você terá aqui. Não estou me referindo a não utilizar os melhores padrões de projeto para evitar gargalos ou criar códigos pobres por preguiça, mas sim, deixar que a necessidade de otimização aconteça antes de se preocupar com isso.

Da mesma maneira que antes de você começar a fazer otimizações de Garbage Collector e da Virtual Machine, seria interessante você ter certeza de que o seu código não consegue ser otimizado ou melhorado.

Para ter certeza de que o problema não está em seu código, ou que não existem melhorias a serem feitas, você pode utilizar monitoradores de performance como: jstat, VisualVM com VisualGC Plugin e HPJMeter.

2 — Comece pelos gargalos maiores

Sei que algumas vezes vai ser necessário fazer pequenos ganhos, seja para aumentar a moral do time ou mostrar para o cliente trabalho, mas por experiência própria, é melhor começar as prioridades pelo que vai trazer maior benefício para a performance da aplicação e depois para as que não terão tanto impacto, porque na maioria das vezes você talvez nem precise fazer a lista completa para que o seu sistema esteja rápido o suficiente.

3 — Use StringBuilder ou StringBuffer para concatenar Strings

Eu não poderia deixar de colocar esta dica como uma das primeiras, como você pode ver no início do post, foi um problema que enfrentei, e este é o tipo de problema difícil de encontrar, porque depois de criado, ao menos que você olhe com atenção o código, com certeza passará desapercebido.

Mas qual o problema com o Objeto String?

No Java o Objeto String é criado de maneira imutável. Sim, é isso mesmo, ele é imutável, portanto cada vez que você concatena uma String com outra, seja utilizando + ou += o Java terá que concatenar o que você quiser e criar um novo objeto, deixando assim o objeto anterior para o Garbage Collector.

Mas então, qual eu deveria utilizar, StringBuilder ou StringBuffer?

Se você estiver apenas concatenando Strings utilize StringBuilder. É um objeto fácil de ser trabalhado e apresenta uma boa performance, inclusive melhor do que StringBuffer. A diferença é que StringBuilder não é ThreadSafe, se você precisar de um Objeto ThreadSafe use StringBuffer.

4 — Use primitivos sempre que possível

Quando você instancia um objeto ele fica alocado na memória HEAP da JVM, inicialmente em um local chamado New Area e se ficar vivo por tempo suficiente ou for um objeto grande passará a ser alocado na Old Area, um local onde o Garbage Collector tem maior dificuldade para fazer sua varredura, assim, demorando mais para destruir e verificar os objetos lá alocados.

Enquanto que os primitivos são guardados diretamente na memória Stack da JVM. Portanto, se você criar um int ele ficará diretamente na memória Stack enquanto se for um Integer a memória Stack terá uma referência para um Objeto que estará na memória Heap e esse objeto conterá o valor.

5 — Verifique os seus logs

Sei que parece bem óbvio, mas você encontrará muitos desenvolvedores que ignoram essas informações preciosas. Procure não só por informações que possam ser úteis para a performance do seu serviço mas por logs sendo gerados sem necessidade e consumindo recursos preciosos para sua aplicação.

Um exemplo clássico é guardar logs que só seriam necessários caso a aplicação estivesse rodando em estado de Debug, por exemplo:

Ao invés de fazer isso e gastar recursos guardando logs que não vão aparecer.

log.debug(“Projeto X — Usuário [” + adminName + “] : Action — [” + action + “]”);

Faça isso

if (log.isDebugEnabled()) {
log.debug(“Projeto X — Usuário [” + adminName + “] : Action — [” + action + “]”);
}

6 — Utilize StringUtils.replace ao invés de String.replace

Em geral o método String.replace é bastante eficiente. Mas, se sua aplicação utiliza muitas operações de replace talvez faça sentido você dar uma olhada em alternativas, principalmente se você ainda usa versões mais antigas como o Java 8.

Um bom candidato para isso é o StringUtils.replace da Apache Commons Lang’s. Ele melhora drasticamente a performance do String.replace do Java 8 e anteriores.

Se você está utilizando Maven em seu projeto, você só precisará adicionar a seguinte dependência no seu pom.xml

<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>

7 — Faça um cache de recursos caros

fazer Caching é uma solução popular para evitar repetições desnecessárias de recursos que custam caro. A idéia é que reutilizar alguns objetos é mais barato do que recriá-los.

Um exemplo comum para o uso disso é por exemplo conexão com banco de dados, a criação de uma nova conexão levará segundos preciosos que poderiam ser evitados apenas não fechando a conexão.

Agora, tenha em mente que na computação, você deve sempre equilibrar o uso dos seus recursos. No exemplo acima, ao guardarmos as conexões isso acarretará em maior uso de memória, da mesma maneira que você deverá administrar essas conexões, portanto escolha sabiamente os recursos que valem a pena estarem no cache.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade