Agora que parei de rir da piada do “implementar GC”, vamos lá…
Não conheço a tua app, então tudo que vou sugerir é mero “achômetro”, e cito isso baseado em experiências passadas. Aliás, falando em experiências em performance, uma boa sugestão é o www.javaperformance.com.br, do Jonas Abreu.
O Spring é muito bom, porém ele foi o primeiro DI existente, e claro, carregar esse legado tudo tem um cusco. Guice é bem mais leve para os usos do dia a dia. Fiz uns testes usando o Apache AB e tem uma boa diferença.
Dê uma analisada no modelo do banco de dados. Um exemplo bem recente que te dou é um blog que eu tenho que é escrito em Java. Nele tenho, na barra lateral, um ponto onde exibo os últimos comentários do blog, algo como “Fluano comentou em Performance Java”. Sendo assim pelo padrão do Hibernate era feito um select nos comentários gerando um select * from BlogComment, depois para cada comentário era executado um select * from BlogEntry where id = ?. Para os 7 comentários exibidos eram executados 8 consultas, o famoso N+1. Note que além disso ambas entidades possuem muitos campos, e eu só exibia 3 campos de cada um. O correto seria usar um fetch join, porém há um bug no JPA critéria que não funciona isso, então o que fiz foi carregar somente as propriedades que preciso e todas de uma vez: https://gist.github.com/2387828.
Ainda com gancho no assunto, se tua tabela está muito grande, particione a tabela. Pegando como exemplo uma tabela de cliente que possui 100 campos, defina os mais importantes e mais acessados e crie uma tabela CLIENTE e outra CLIENTE_DETALHE com os dados extras. Isso porque você corre o risco de carregar toda uma tabela para ler apenas três campos, algo como acontecia no exemplo acima.
Evite também fazer muitas transformações para exibir dados na tela. No meu blog, por exemplo, a cada comentário mostro um avatar do usuário com a foto do Gravatar, que é calculado usando um MD5 encima do email. Ao invés de fazer isso em tempo de execução, eu já gravo o comentário com o hash no banco, pois assim evito recalcular o campo toda hora. Calcular esse hash é bem rápido, porém imagine 50 comentários em uma tela com 1mil clientes acessando…
Indíces: isso é uma coisa que ninguém se preocupa, mas é muito importante. Em sistemas que você tem muitos acessos é muito importante achar os melhores indices. Um bom começo é fazer um levantamento de todas as consultas necessárias no sistema e montar indices baseado nelas. Outra coisa muito importante: analise se o banco está usando os tipos corretos, e dependendo do banco um determinado tipo possui performance melhor que outros. Por exemplo, no MySQL definir campos booleans como byte é melhor que int(1). No Oracle, por exemplo, você pode gerar um plano de execução, e com base nos dados mostrados, você terá informações para otimizar bem os índices.
Mandando o Hibernate imprimir os comandos SQL é uma boa opção para você fazer um tunning. O problema acima detectei assim, acessando localmente o site e analisando as queries e pensando sempre em diminuir ao máximo a quantidade de consultas ao carregar uma única página. Outra sugestão é o Hibernate Statistics, onde você habilita este recurso que te mostra o uso da Session, objetos em memória, entidades carregadas, dirty objects, etc.
Outras questãos de Hibernate: configuração.
- Habilitar logs detalhados somente em desenv. Em PRD/HML use o logger como WARN. Aliás, qual implementação de logging você usa? Isso pode impactar muito na performance, já que logging gera muito I/O. Aliás, você sabe que os frameworks de logging normalmente fazem log síncrono? Uma opção é configurar o logging para asíncrono (no JBoss basta uma única linha no logging.properties). Outro detalhe: o log4j + commons-logging são péssimos em performance.
- Você usa algum cache de segundo nível? Talvez seja importante analisar esta questão caso você tenha muitos acessos a uma determinada consulta. Configurando bem o 2nd level cache para as consultas mais utilizadas você pode ter um bom ganho.
Você usa tomcat para uma aplicação crítica? Repense seus conceitos e use um Application Server de verdade. Não é que eu tenha preconceito com o Tomcat ou Jetty, porém tem certos assuntos que adultos devem resolver. Se você tem algo crítico, é para isso que serve um application server.
Um ponto muito importante: não manipule nada de infra na tua aplicação. Infra é de responsabilidade do Application Server. Deixe que ele gerencie conexões de banco, mais sessions, transações, deixe que ele gerencie o ciclo de vida destes recursos, e sempre que você precisar algo de infra, peça para ele. Além disso todos os Application Servers possuem monitores de uso das aplicações, inclusive de conexões de banco, recursos JMS, servlets/filters, EJBs, e tudo mais.
Por mais que o VRaptor seja fantástico em trabalhar com o modelo, dependendo do que tua aplicação crescer, talvez seja a hora de pensar em um EJB ou CDI para controlar o ciclo de vida dos objetos do modelo. Tenho usado um mix de CDI com EJB em vários projetos meus e não tenho o que reclamar. E o VRaptor faz o lookup destes objetos tranquilamente.
Já no lado da infra, outra coisa a observar é como está configurado as instâncias de tua aplicação. Delegar, por exemplo, 4G para uma aplicação é um tiro no pé. Aliás, é um tiro na cabeça. Lembro que em um treinamento que fiz há dois anos atrás sobre JBoss AS, comentamos sobre um caso peculiar. Alguém alocou 20G de memória para uma aplicação. Tudo funcionava bem, até que os 20G lotada. Aí quando o GC iria passar, a aplicação congelava até o GC passar, e isso levava horas. Não lembro se os valores era bem esses, mas é algo assim. O mais correto é você ter várias pequenas instâncias da aplicação com um load balance.
Obviamente o assunto pode ser melhor discutido, mas o que lembro até agora é isso. Ainda mais que não conheço a tua APP. E como dizem por aí, não existe receita de bolo para otimização. Porém tomando pequenos cuidandos você tem uma aplicação bem próxima ao ideal.
E não pense que é só código ruim que faz a aplicação ter problemas de performance. Muitas vezes é simplesmente configuração. O Hibernate pode sim ter culpa nos problemas de performance, pois se ele estiver mal configurado, será ele o gargalo.