sexta-feira, 5 de setembro de 2008

Bug Estouro de Memória - Cursores no Oracle com Java

Caros visitantes.
Não costumo publicar posts longos, porque o objetivo deste BLOG é de "DICAS RÁPIDAS"... Mas desta vez vou abrir uma exceção. Como devem ter notado, no último mês não houve postagem de conteúdo algum. A verdade é que tive sérios problemas com uma aplicação em Java 1.4, rodando num ambiente Oracle IAS com driver classes12.jar que reiniciava a cada meia hora por causa de um estouro de memória como este:

Exception in thread "CompilerThread1" java.lang.OutOfMemoryError: requested xxxx bytes for ChunkPool::allocate. Out of swap space?

Estivemos as voltas todo este tempo tentando descobrir o que estava ocupando toda memória, porque logicamente temos swap e memória suficiente na máquina que hospeda esta aplicação.
Nosso maior problema foi que ao monitorar a aplicação, observamos que a memória HEAP não passava de 100Mb (com alto stress da aplicação), porém o processo no Sistema Operacional exibia a utilização de mais de 2Gb de RAM. Passamos um mês procurando agulha no palheiro, abrindo cada coleção em memória e cada objeto suspeito para ver se poderia estar provocando um leak.

Nossa primeira desconfiança foi a de que muitos objetos estavam saindo da aŕea de memória HEAP para a NON-HEAP por algum motivo, - provavelmente objetos estáticos-. Mas para nossa frustração, todos os objetos notadamente estáticos que poderiam estar em memória apareciam na HEAP. Então o que poderia estar causando essa utilização vertiginosa de memória???

Foi quando -depois de centenas de tentativas de correção - graças a uma casualidade tivemos uma exception diferente.... uma exception de banco do tipo ORA-100. (Muitos cursores abertos). Tudo bem, encontramos um bug no sistema, uma de nossas consultas ao banco não estava fechando Resultsets e Statements, embora fechando corretamente a conexão. Fixamos o bug e tal foi nossa surpresa que a memória voltou a o normal,... aqueles 150Mb que víamos nas ferramentas de monitoria era muito próximo ao exibido no Sistema Operacional.

Concluímos então que de alguma forma, mesmo fechando a conexão, e mesmo não utilizando cursores em nosso sistema, o driver ou o IAS de alguma forma alocam memória física local para utilização de RecordSets e PrepareStatements que não ficam nunca disponíveis ao GarbageColector, visto que não observamos sequer um destes objetos na monitoria da memória.

Fica a dica, quando um problema de memória for detectado, e as monitorias apontarem que a JVM está utilizando muito menos memória HEAP e NON-HEAP do qua a apresentada pelo Sistema Operacional, verifique os cursores de sua aplicação. Um único método é capaz de acabar com sua aplicação inteira.... e sua tranquilidade também. ;-)

Até a próxima.

Nenhum comentário: