domingo, 10 de agosto de 2008

Tagged under: , ,

Resolvendo problemas de performance (parte 1)

É comum nos depararmos com aplicações construídas em Java que são lentas, pesadas e consumidoras vorazes de memória. Muitos, simplesmente, culpam a plataforma como a causadora de todos os problemas mas, na verdade, a culpa está sempre no desenvolvedor que não procura conhecer por que sua aplicação está sem performance.

Um dos problemas mais comuns de acontecer é o OutOfMemoryError, a única exceção em Java que não tem chance de ser tratada. Quando essa exceção é lançada, sua aplicação provavelmente sai do ar e não retorna.

Vamos conhecer mais sobre o mundo do Garbage Collector do Java e entender como podemos resolver problemas de performance em nossas aplicações.

Gerenciamento de memória da Sun JVM

A máquina virtual Java da Sun (Sun JVM) é geracional, ou seja, um objeto é criado num espaço inicial e muda para outros espaços, até que chegue num espaço onde possa morrer quando não for mais usado. A Sun JVM é dividida nos seguintes espaços: Young generation, Old generation, Permanent generation. Os dois primeiros espaços compõem o chamado Heap - onde os objetos são criados pelos desenvolvedores - e é onde nós vamos nos focar nesse momento.


O espaço Young generation - geração jovem - é subdividido em outros três espaços: o Eden space, o Survivor From space e o Survivor To space. Um objeto sempre é criado no Eden - literalmente no paraíso. Quando o Eden está cheio, o Garbage Collector (GC) percorre esse espaço e verifica se os objetos ainda estão sendo usados. Os que não estiverem mais sendo usados são removidos da memória e os que ainda estiverem em uso vão para o primeiro espaço de sobrevivência (Survivor From). Quando o Eden novamente fica cheio, o GC copia os objetos em uso para o segundo espaço de sobrevivência (Survivor To) e remove os objetos sem uso. Quando os espaços Survivor estão cheios, o GC copia os objetos que estiverem ainda em uso para o Tenured space - o espaço da estabilidade da geração velha (Old generation).



Esse processo de cópia que o GC realiza é conhecido como Copy Collection (coleta de cópia) ou Minor Collection (coleção menor). Quando o GC não pode mais realizar uma copy collection com os objetos (provavelmente os espaços já estão cheios), ele realizará uma Major Collection (coleção maior), também conhecida como Stop-The-World Collection (coleta pare-o-mundo). Durante essa coleta, o GC suspende todas as threads na Sun JVM e realiza uma busca na memória mais profunda para encontrar os objetos que realmente não estão mais sendo usados e, assim, liberar memória nos espaços.


Na implementação da Sun JVM, percebe-se que os objetos que estiverem no Tenured Space só podem ser removidos por uma coleta de lixo maior. Objetos que chegam até esse espaço de memória são objetos custosos e nós queremos evitar que isso aconteça para que a memória fique o mais limpa possível (diminuindo o trabalho do GC).

Memory leaks

Agora que já sabemos como funciona a memória, vamos entender o que é um memory leak (gargalo de memória). A memória está "engarguelada" quando um objeto mantêm uma referência indesejada para outro objeto, o que força o GC a não liberar a memória usada. Os objetos que não são desreferenciados farão seu caminho através dos espaços Eden e Survivor até chegarem à geração velha. Lá, eles ficarão e permanecerão porque o GC acha que os objetos ainda estão em uso. Para complicar, se isso acontecer num ambiente multi-usuário (como uma aplicação web), muitas requisições feitas usando um código que "enguargela" os objetos farão com que o tenured space cresça sem parar.

Para identificar um memory leak em nossa aplicação, temos que usar uma ferramenta Profiler. Algumas IDE's já possuem um profiler integrado mas, se não for o seu caso, a Sun JVM já traz um modo de você rastrear os seus objetos através do console do seu sistema operacional. Basta apenas iniciar a JVM com o seguinte argumento:
–verbose:gc

Quando sua aplicação estiver rodando, a JVM produzirá no console mensagens como essa:
[GC 325407K->83000K(776768K), 0.2300771 secs]
[GC 325816K->83372K(776768K), 0.2454258 secs]
[Full GC 267628K->83769K(776768K), 1.8479984 secs]


Aqui nós vemos duas coleções menores seguidas por uma maior. Os números antes e depois da seta (325407K->83000K na primeira linha, por exemplo) indicam o tamanho do objetos vivos antes e depois da coleta de lixo. O próximo número em parênteses ( (776768K) novamente na primeira linha) é a quantidade de memória realmente usada pelos objetos - não confudir com a memória que foi alocada no sistema operacional. Esse número não inclui a permanent generation (falaremos sobre ela em outra oportunidade). O último item da linha (0.2300771 secs) diz qundo tempo foi gasto pelo GC para fazer a sua tarefa.

Se você acrescentar os seguintes argumentos, você irá acrescentar mais detalhes às informações produzidas pelo GC, inclusive com referência temporal (timestamp):
–XX:+PrintGCDetails –XX:+PrintGCTimeStamps

A saída produzida no console parecerá com isso:
111.042: [GC 111.042: [DefNew: 8128K->8128K(8128K), 0.0000505 secs]111.042: [Tenured: 18154K->2311K(24576K), 0.1290354 secs] 26282K->2311K(32704K), 0.1293306 secs]

Onde você vê a palavra DefNew significa que uma coleção menor foi realizada; onde se vê a palavra Tenured significa que uma coleção maior foi realizada. Após essas palavras, é mostrado o tamanho da memória antes e depois do GC naquele espaço e, no final da linha, sobre todo o heap. A informação no início da linha (111.042) é a referência temporal de quando aquela atividade aconteceu.

Até mais

Posteriormente, continuaremos esse assunto trazendo dicas de como evitar os memory leaks e como monitorar sua aplicação com a ferramenta JConsole que já vem na Sun JVM. Também aprenderemos mais sobre os três tipos de Garbage Collector existentes na Sun JVM e quando usá-los.

1 comentários:

Anônimo disse...

Olá pessoal aqui é professor Marcos, do Professordelphi, rua das Dalias, 516, Pituba, aqui no Centro de Treinamento de Programadores serão abertas novas turmas de JAVA, JSP e linguagem C para o mês de agosto, os interessados deverão fazer suas matriculas com urgência para não ficar de fora do mercado de trabalho exigente e competitivo, onde só os melhores sobrevivem.

Para maiores detalhes acesse o site: www.professordelphi.com ou ligue para 71 3353-5600, procurar a Lúcia ou Cecília.

Observação fechamos parceria com a CPM BRAXIS para indicar os nossos alunos.