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

29 respostas
J

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?

29 Respostas

Felagund

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.

E

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

E

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.

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.

J

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.

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 ;
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.

CarlosEduardoDantas

juliocbq:
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.

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 ;

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.

J

[quote=CarlosEduardoDantas]

juliocbq:
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.

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 ;

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.

E

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.

J

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.

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.

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.

J

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.

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!?

ViniGodoy

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

ViniGodoy

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.

CarlosEduardoDantas

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

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.

J

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:

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);

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

J

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

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.

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.

E

Image.flush

J

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

ViniGodoy

As documentações do Java 2D tem recomendado usar o ImageIO.read para carregar um BufferedImage, e não mais o Toolkit. A gerência do BufferedImage costuma a ser mais eficiente (pelo menos é o que dizem, nunca rodei nada para testar, mas não tem pq desconfiar da própria Sun).

CarlosEduardoDantas

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

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.

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.

eu não tinha visto teu código acima… mas tenta usar o que o colega abaixo postou e nos notifique dos resultados

boa sorte :wink:

J

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

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.

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.

eu não tinha visto teu código acima… mas tenta usar o que o colega abaixo postou e nos notifique dos resultados

boa sorte :wink:

estou constantemente online. Daqui a pouco posto os resultados do teste.

ViniGodoy

juliocbq:
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.

Vai passar null para quem?
Geralmente, são poucas vezes que você deve fazer = null explicitamente. Exceto se você tiver guardado imagens num vetor, ou em atributos que não sairão de escopo. Mas não parece ser o seu caso…

dm_thiago

Me lembro que no livro Effective Java, o autor falava sobre esse tipo de problema. Se eu não me engano, o exemplo dele era uma lista que ele guardava objetos, mas usava uma variavel inteira para apontar o final da lista. Então o problema era que, quando se removia elementos, a variavel regredia o valor, mas não era exluído da lista, ou seja, o objeto sempre tinha referencias e nunca era recolhido pelo GC (é melhor olhar o livro porque eu não lembro muito bem não hehehe). Você podia ver se não tem algum problema parecido no seu código, onde a referencia a seus objetos são guardados em algum lugar e são esquecidos lá.

J

ViniGodoy:
juliocbq:
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.

Vai passar null para quem?
Geralmente, são poucas vezes que você deve fazer = null explicitamente. Exceto se você tiver guardado imagens num vetor, ou em atributos que não sairão de escopo. Mas não parece ser o seu caso…

Normalmente, passar null para um objeto faz com que o coletor de lixo o enxergue. Eu usei o metodo flush, no final de cada decodificação, e deixei o software rodando. Vamos ver se vai estourar o heap.

B

Outra idéia para usar referências é usar as classes de java.lang.ref, e trabalhar com seus vários níveis de referências.

As referências mais fracas são usadas para objetos que você gostaria que estivessem em memória, mas podem ser recolhidos caso o sistema precisar de memória.

J

O estouro voltou a acontecer, mas dessa vez não foi dentro de BufferedImage. Recebi apenas essa exceção. Vou tentar otimizar o código ao máximo, de acordo com as idéias do vinnie. Utilizarei o profile, e trocarei toolkit por ImageIO

Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space

Deixarei o software rodando a noite, e amanhã terei mais novidades.

Obrigado a todos pela ajuda…

J

Que dureza pessoal. O problema do heap eu consegui resolver. Mas o software agora congela por causa do coletor de lixo algumas vezes.
Alguém ae sabe como consigo liberar da memória de um vetor de bytes?
byte[].

J

Obrigado a todos pelos posts.

Criado 7 de outubro de 2009
Ultima resposta 9 de out. de 2009
Respostas 29
Participantes 7