Performance no Java
Todos desejamos performance nas nossas aplicações. No Java e apesar de alguns dizerem que é mais rápido que C++ isto nem sempre é verdade, mas vou falar disso noutra altura. Hoje o que queria colocar aqui é uma ajuda para quem tem que trabalhar com Java. Em vez de correr a JVM client o melhor para performance é mesmo correr a JVM server. Para isso terá que utilizar o terminal e correr os programas da seguinte forma:
java -server -jar programa.jar
Ora para não ter que andar sempre a adicionar -server no terminal, o melhor é colocar o -server como default. Para isso basta editar o ficheiro jvm.cfg (que é um ficheiro de texto simples) e onde está:
-client KNOWN
-server KNOWN
é só trocar a ordem para:
-server KNOWN
-client KNOWN
Assim tudo o que for java vai correr na JVM server e só se chamarmos explicitamente a versão client é que será corrida aí.
Ganhos de performance? Sim bastante notáveis. Estive a experimentar calcular uma série de Fibonacci e posso dizer que o tempo de execução foi em média:
-client: 540ms
-server: 397ms
o que dá para o mesmo programa um ganho de 143ms ou cerca de 26%, nada mau. O downside? Tradicionalmente o JVM -server demora um pouco mais a arrancar e come mais memória, mas se depois se tornar mais rápido… para quê queixarmo-nos…
no Mac OS X Tiger o jvm.cfg está em
/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/jvm.cfg
Bem, por curiosidade o mesmo cálculo em C sem optimizações demorou 810ms e com optimizações -O3 cerca de 260ms. Mas como disse estas discussões são para outra altura.
Leia Também:
- Sun prepara Java para iPhone Já tinha questionado há algum tempo se o...
- iPhone sem Java um Erro? Java for iPhone urged by Sun official...
- Java4Mac… Sun devia fazê-lo Para as restantes plataformas o Java já...
Secção: java | Partilhar no FriendFeed

Viva,
Outra coisa fácil de configurar e que costuma resultar em ganhos na performance é o tamanho inicial e maxímo da heap, para isso basta-te configurar -Xms (initial heap size) e -Xmx (maximum heap size). Podemos assim evitar que o GC entre em acção mesmo quando existe memória que poderia ser usada pela JVM.
Poderias também entrar em assuntos como o tunning do GC, mas isso é daquelas “artes mágicas” algo complexas (pelo menos eu acho, mas também não sei assim tanto sobre o assunto) que ou sabemos realmente o que estamos a fazer ou então em vez de melhorarmos pioramos a performance.
Ah já agora, talvez já saibas mas fica aqui a dica, a JVM possui um profiler muito simples que podes usar quando estás a fazer estes testes, o JConsole. Para o usares basta começares a jvm com mais um switch: -Dcom.sun.management.jmxremote. Depois enquanto o programa java estiver a correr, basta lançares o jconsole (que está no $JAVA_HOME/bin).
A server-vm tem uma melhor performance do que a client-vm, mas o consumo de memória (que pode ser muito maior) e o tempo extra de arranque das aplicações pode negar esse ganho. Para aplicações que vão correr durante muito tempo, praticamente sozinhas, numa máquina com memória em grande quantidade, a server-vm é claramente melhor.
Para aplicações desktop, se calhar não. Ganhas na performance das aplicações Java, e perdes no resto do sistema. Para o utilizador é capaz de ficar tudo na mesma.
O Carlos tem toda a razão.
Que tal colocares o resultado do:
time java -server aplicacao
time java -client aplicacao
Pois, eu digo isso, performance vem à custa de recursos. Não há volta a dar-lhe. Ou se tem ou não se tem. Agora se alguém se dá ao trabalho de editar um ficheiro de configuração de JVM provavelmente é porque utiliza aplicações Java mais do que o simples applet ocasional. A meu ver quem estiver todo o dia enfiado num eclipse ou netbeans ou … etc… se calhar já começa a compensar a mudança. No caso do Netbeans 6 RC posso dizer que a diferença é notória.
Claro que é preciso ter recursos, mais uma vez, não se fazem omoletes sem ovos.
@Gustavo,
Os resultados que pedes estão lá escritos no post. Ou será que não leste bem?Desculpa, os valores lá não eram do time… mas cá vão..
-Client
real 0m0.667s
user 0m0.610s
sys 0m0.038s
-Server
real 0m0.569s
user 0m0.506s
sys 0m0.042s
Estes valores foram corridos hoje, apenas uma vez com uma série de tralhada diferente… mas mesmo assim continua a ver-se melhorias da versão server.
To David: Está visto que não me fiz perceber. Por isso vou tentar outra vez e até incluo código:
public class Primos {
public static void main(String[] args) {
int n = 100000;
boolean primo;
int i,z;
long inicio, fim;
inicio = System.currentTimeMillis();
for (i = 1; i <= n; i++) {
primo = true;
for(z = 2 ; z <= i/2 && primo; z++ ){
if(i%z == 0)
{
primo=false;
}
}
}
fim = System.currentTimeMillis();
System.out.println(”Demorou ” + (fim-inicio)/(float)1000 + ” segundos de tempo total.”);
}
}
Este código vai indicar o tempo que a JVM esteve efectivamente a trabalhar. Comparar os outputs com -client e -server vai mostrar se existe um aumento de performance.
Ao usar o “time java -client Primos” e “time java -server Primos” vai nos mostrar o overhead no arranque da JVM. Os tempos que tu indicas devem ser apenas os reportados pela aplicação.
@Gustavo,
Os tempos do post são os dados pelo programa tal e qual como no teu exemplo.
Os tempos do meu comentário são os da utilização do time java…
O problema destes “benchmarks” sintéticos é que o processamento é demasiado focado para se conseguir tirar conclusões válidas. Os testes correm durante pouquíssimo tempo, não geram grandes alocações de memória, e resumem-se a umas quantas linhas de código que, com um pouco de sorte, uma vez JITadas cabem dentro da cache do CPU.
Porque como eu dizia, se tens um Eclipse a consumir quantidades obscenas de memória, e tens um Firefox ao lado com algumas tabs e mais uma coisa ou outra e não tens 4Gb de RAM, se calhar vais começar a usar swap, quando com a client VM cabe tudo na memória e a perda de performance não se nota (porque a aplicação passa a maior parte do tempo à espera de input).