Java.lang.OutOfMemoryError: Java heap space[resolvido]

Bom dia,

Estou fazendo um teste de stress num software de segurança, que estamos desenvolvendo. Esse software foi escrito em java, e roda 24 horas / dia.
Deixei o software a noite rodando e recebi essa exceção:

Exception in thread "Image Fetcher 0" java.lang.OutOfMemoryError: Java heap space at java.awt.image.DataBufferInt.<init>(DataBufferInt.java:41) at java.awt.image.Raster.createPackedRaster(Raster.java:458) at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1015) at sun.awt.image.ImageRepresentation.createBufferedImage(ImageRepresentation.java:230) at sun.awt.image.ImageRepresentation.setPixels(ImageRepresentation.java:484) at java.awt.image.ReplicateScaleFilter.setPixels(ReplicateScaleFilter.java:233) at sun.awt.image.ImageDecoder.setPixels(ImageDecoder.java:120) at sun.awt.image.JPEGImageDecoder.sendPixels(JPEGImageDecoder.java:97) at sun.awt.image.JPEGImageDecoder.sendPixels(JPEGImageDecoder.java:97)

O que eu vejo aqui é que a vm não aguenta decodificar imagens jpg a todo instante. Alguém já teve um problema como esse e conseguiu resolver?

Você pode aumentar a memoria da JVM que por padrão é 256 ( ou 128) MB, pasta usar o paramentro -Xmx512m, para usar 512 MB de memoria, -Xmx1024m para usar 1GB de memoria, e por ai vai.

http://forums.sun.com/thread.jspa?threadID=5353268

Isso só retarda o aparecimento do problema. Se houver um “vazamento de memória” (comum quando se usam APIs que usam métodos nativos e não documentam claramente o que deve ser feito para liberar os recursos) o problema aparecerá mais cedo ou mais tarde.

Se as imagens que você recebe do sensor não forem colocadas como candidatas ao Garbage Colllector, mais cedo ou mais tarde aparecerá este erro.

[quote=CarlosEduardoDantas]Se as imagens que você recebe do sensor não forem colocadas como candidatas ao Garbage Colllector, mais cedo ou mais tarde aparecerá este erro.
[/quote]

Não estou usando nenhum recurso nativo ou api. É um BufferedImage mesmo. No stack trace podem ver que ocorre dentro da api do java.

Cococar a imagem como candidata ao gc seria atribuir null a ela?

Ex:

bi = null ;

Em sistemas críticos é uma boa iniciar a VM com a seguinte flag:
-XX:-HeapDumpOnOutOfMemoryError

Isso vai gerar um HeapDump, assim que o erro ocorrer. Aí você pode abrir esse dump no Netbeans ou no Visual VM, e observar que objetos estão ocupando muita memória em seu sistema, rastreando assim a causa.

Se isso ainda não adiantar, talvez seja interessante rodar o sistema no profiler do Netbeans e usar a opção “Trace memory allocation” (ou algo parecido). Isso permitirá que você rastreie a árvore de alocação de seus objetos, e saiba quem está mantendo as referências do sistema. É muito comum que variáveis estáticas mantenham gerem leaks.

[quote=juliocbq][quote=CarlosEduardoDantas]Se as imagens que você recebe do sensor não forem colocadas como candidatas ao Garbage Colllector, mais cedo ou mais tarde aparecerá este erro.
[/quote]

Não estou usando nenhum recurso nativo ou api. É um BufferedImage mesmo. No stack trace podem ver que ocorre dentro da api do java.

Cococar a imagem como candidata ao gc seria atribuir null a ela?

Ex:

bi = null ; [/quote]

eu creio que este seja seu problema, pois como tu disse, seu sistema roda 24 hrs por dia, logo recebe imagens do sensor a todo instante. Imagino que cada imagem recebida, acredito que você trata o conteúdo em outra thread, e depois não precisa mais da imagem jogando para o banco e descartando-a.

Se este BufferedImage já recebe diretamente outra imagem, a anterior se torna uma candidata ao Garbage Collector, agora se for uma lista, ou algo assim precisa avaliar direito. Acredito que estas imagens que você já tratou não estão sendo coletadas para o lixo, com isso gerando um erro de memória depois de algum tempo processando.

[quote=CarlosEduardoDantas][quote=juliocbq][quote=CarlosEduardoDantas]Se as imagens que você recebe do sensor não forem colocadas como candidatas ao Garbage Colllector, mais cedo ou mais tarde aparecerá este erro.
[/quote]

Não estou usando nenhum recurso nativo ou api. É um BufferedImage mesmo. No stack trace podem ver que ocorre dentro da api do java.

Cococar a imagem como candidata ao gc seria atribuir null a ela?

Ex:

bi = null ; [/quote]

Sim, essas imagens estão em uma lista ligada. Vou checar isso.

eu creio que este seja seu problema, pois como tu disse, seu sistema roda 24 hrs por dia, logo recebe imagens do sensor a todo instante. Imagino que cada imagem recebida, acredito que você trata o conteúdo em outra thread, e depois não precisa mais da imagem jogando para o banco e descartando-a.

Se este BufferedImage já recebe diretamente outra imagem, a anterior se torna uma candidata ao Garbage Collector, agora se for uma lista, ou algo assim precisa avaliar direito. Acredito que estas imagens que você já tratou não estão sendo coletadas para o lixo, com isso gerando um erro de memória depois de algum tempo processando.

A rigor está sim. Exemplos de APIs do Java que usam recursos nativos:

Quase todas as classes do pacote java.io.*
Quase todas as classes do pacote java.net.*
Quase todas as classes do pacote javax.imageio.*
Quase todas as classes que têm “Image” no nome

e assim por diante.

Essas APIs você sempre precisa examinar para ver se existe algum método “close”, “dispose”, “flush” ou “remove” para efetuar a devolução de recursos nativos ao sistema operacional.

[quote=ViniGodoy]Em sistemas críticos é uma boa iniciar a VM com a seguinte flag:
-XX:-HeapDumpOnOutOfMemoryError

Isso vai gerar um HeapDump, assim que o erro ocorrer. Aí você pode abrir esse dump no Netbeans ou no Visual VM, e observar que objetos estão ocupando muita memória em seu sistema, rastreando assim a causa.

Se isso ainda não adiantar, talvez seja interessante rodar o sistema no profiler do Netbeans e usar a opção “Trace memory allocation” (ou algo parecido). Isso permitirá que você rastreie a árvore de alocação de seus objetos, e saiba quem está mantendo as referências do sistema. É muito comum que variáveis estáticas mantenham gerem leaks.
[/quote]

Vou usar o profiler e verificar o que me disse. Como citado acima, essas imagens estão em uma fila circular. Mas imagino que esse problema esteja vindo diretamente do BufferImage, na hora da decodificação do arquivo. Essas imagens nem vem de sensor algum. São somente recursos contidos em disco.

Não adianta atirar no escuro…

Outra possibilidade é que não seja mesmo um problema de leak. Mas de falta de memória. Imagens geralmente ocupam muita memória e, por padrão, a VM tem um heap irrisoriamente pequeno, de apenas 64mb.

Algumas fases da imagem fazem a descompactação de seu conteúdo. Então um png que no disco pode ser pequeno, se expande na memória.

[quote=ViniGodoy]Não adianta atirar no escuro…

Outra possibilidade é que não seja mesmo um problema de leak. Mas de falta de memória. Imagens geralmente ocupam muita memória e, por padrão, a VM tem um heap irrisoriamente pequeno, de apenas 64mb.

Algumas fases da imagem fazem a descompactação de seu conteúdo. Então um png que no disco pode ser pequeno, se expande na memória. [/quote]

Sim. A fila tem 6 posições. Vou ver o que consigo fazer para liberar essas imagens da stack da vm.
Mais uma vez caímos na velha discução do coletor de lixo, hein!?

Heheheh, pois é. Mas ele é muito confiável.
Se as imagens estão ficando presas, é porque algo as referencia.

Aliás, o profiler também as vezes revela suspresas. Uma época tinhamos um estouro de memória num componente de textos, e acabamos descobrindo que o problema não era o componente.

Veja bem, se alguma outra coisa estiver com leak na sua aplicação, pode ocorrer que um processo que gere um pico um pouco maior (como a carga de uma imagem) estoure, não por culpa dele, mas porque o heap já está cheio de outro tipo de lixo. Por isso, o stack trace de um OutOfMemoryError é geralmente pouco relevante.

[quote=ViniGodoy]Heheheh, pois é. Mas ele é muito confiável.
Se as imagens estão ficando presas, é porque algo as referencia.[/quote]

Vini, só para constar. Eu não questionei a confiabilidade do coletor, mas conheço o processo e sei que ele não vai coletar algo que você não descarta. Isto é falha do sistema.

Olha como funciona:

Para guardar a imagem no banco de dados, uso o codificador base64 e a transformo em string. A imagem fica pequena como string.
Depois faço o caminho inverso:

[code]
byte b[] = Base64.decode(lstLogAcesso.get(0).getUsuario().getFoto());

                Image img = Toolkit.getDefaultToolkit().createImage(b);// o stack trace aponta o problema aqui. Justamente onde o estouro do heap acontece.
                Image rsz = img.getScaledInstance(114, 158, 0);
                ImageIcon imgIcon = new ImageIcon(rsz);

                lbFotografia.setIcon(imgIcon);[/code]

Vou tentar desalocar esse objeto, e testar novamente no profiler.

[quote=CarlosEduardoDantas][quote=ViniGodoy]Heheheh, pois é. Mas ele é muito confiável.
Se as imagens estão ficando presas, é porque algo as referencia.[/quote]

Vini, só para constar. Eu não questionei a confiabilidade do coletor, mas conheço o processo e sei que ele não vai coletar algo que você não descarta. Isto é falha do sistema.[/quote]

Pode me explicar como descartar o objeto BufferedImage?
O que vou tentar fazer agora, é passar null pera ele, assim não terá mais referência na memória, e o coletor deverá fazer a coleta.

Image.flush

Isso mesmo. Tem aqui na documentação. Vou deixar rodando aqui e ver se estoura até as 12:00 hs.