A Famosa Falta de Memória OutOfMemoryError

Postado em Atualizado em

Hoje eu gostaria de divertidamente abordar o famoso erro de falta de memória frequente das soluções Java. Os dois sabores mais comuns deste delicioso erro são: java.lang.OutOfMemoryError: Java heap space e java.lang.OutOfMemoryError: PermGen. Com isso, vamos nos divertir no assunto:

Gerenciamento de memória em soluções Java é como uma brincadeira de criança. Todo aplicativo Java é executado um cima da JVM que por sua vez esta instalada em cima do S.O. Todo sistema operacional disponibiliza um espaço determinado fixo de memória RAM total da estação para a uso da JVM. Em outras palavras seria afirmar que um programa Java tem disponível para gastar memoria um copo vazio, de tamanho previamente determinado no qual poderíamos exemplificar como um de 300 ml.

Cada vez que o programa Java executando aloca uma variável ou objeto, seria como se ele estivesse gastando espaço nesse copo, adicionado uma gota de água la dentro. Vale lembrar que existem muitas outras coisas dentro de um programa Java que também gasta memória. Nesse sentido, o programa vai sendo usado e o nosso copinho de água vai se enchendo.

Agora vamos para o detalhe mais importante, quando a água ultrapassa a media de 70% do total disponível da memória, a JVM tem um camarada que se chama “Coletor de Lixo” que é avisado para limpar essa memória. Esse coletor de lixo então se prepara e a qualquer momento a partir disso, ele acaba passando para fazer seu trabalho. Voltando para nossa brincadeira, o coletor de lixo é o cara que tem a responsabilidade de esvaziar a água do copo.

Continuando nossa linha de raciocínio, o coletor de lixo no momento sublime e único da solução é executado e assim consegue esvaziar um tanto de memória, reiniciando então o ciclo de gastos. Na visão da nossa brincadeira, o coletor de lixo derramaria então um tanto de água, esvaziando parcialmente o nosso copinho. Esse é ciclo que eu chamo de “marawonderfull”! A aplicação vai sendo executada, gastando memória (adicionado gotinhas de água no copo de 300 ml) e o coletor de lixo vai de tempos em tempos esvaziando uma parte da água. Seria o cenário ideal de uma solução Java.

Como tudo da vida não é perfeito, vamos ao terror! Imagine na sua cabeça agora o que aconteceria se o coletor, dentro dos ciclos de sua passagem não conseguisse esvaziar nada de água? A aplicação vai gastando, gastando, adicionando água e quando você menos espera bummmmm, acontece o “java.lang.OutOfMemoryError” que significa nada mais simples que:

“não é possível continuar executando esse programa Java porque ele ta precisando gastar memória e o copo já esta totalmente preenchido.”

A partir desse ponto de visão é muito mais fácil entender quais são os motivos da falta de memória:

1. A solução esta gastando memória desenfreadamente

O que eu mais vejo nas consultorias e fóruns são os desenvolvedores despreparados, que mesmo bem tensionados acabam escrevendo código que resulte em gastos de memória totalmente desenfreados, sem nenhum critério, preocupação ou um padrão arquitetural previamente estabelecido.

Como corrigir ?

1.1 Alterar o código da aplicação, aplicando sistematicamente melhores práticas diante de cada situação ou elaborar e estabelecer uma arquitetura padrão que contemple práticas de otimização. Que tipos de praticas? Segue algumas:

  • Liberar objetos obsoletos logo após o uso atribuindo null.
  • Reuse um objeto, resetando seu estado, ao invés de recria-lo.
  • Não faça operações matemáticas com wrappers correlativos ao tipos primitivos, uma vez que o auto/unbox vai gerar um novo objeto para cada operação.
  • Identifique e aplique singleton para os objetos únicos da solução ao invés recria-los desnecessariamente para cada processo.
  • Identifique e reuse objetos imutáveis sem identidade da solução ao invés recria-los desnecessariamente para cada processo.
  • Identifique e aumente corretamente o escopo de vida de objetos ao invés recria-los desnecessariamente para cada processo.
  • Identifique e reduza corretamente  o escopo de vida de objetos, disponibilizando mais rapidamente para a  coletor de lixo GC.
  • Usar StringBuffer ou StringBuilder quando manipular algo volume de string.
  • Use a abordagem de paginação ao invés de criar um alto volume de objetos resultando de interações com banco de dados, arquivos txt, xml etc.
  • Use a abordagem de cache para objetos com perfil ao invés de ficar gastando memória desnecessária com repetitivas iterações remotas para recursos externos – SGDB, LDAP, EJB, FILE, etc.
  • Para operações em Lote com SGDB use estrategia de Batch ao inves de processamento manual.

1.2 Elaborar e estabelecer uma arquitetura padrão para a solução que previamente contemple todas as práticas de otimização envolvidas dentro do contexto. Aqui já é necessária a contratação de consultor ou alguma empresa que faça o papel de um arquiteto.

2. A solução já esta otimizada, mas ainda não é suficiente. O que fazer?

Caso a solução já esta 100% otimizada, entenda que a única coisa que pode ser feito é aumento da memória. Ou seja, se a solução esta gastando o mínimo possível e ainda assim esta precisando de mais memória, a unica resposta lógica e cabível é que o copo de 300 ml não é suficiente para executar essa determinada solução. Isso não é o fim do mundo, muito pelo contrario, é algo totalmente corriqueira e normal. Situação muito comum em aplicativos Java para Web no qual o número de usuários simultâneos é gradativamente crescente no tempo de vida de existência da solução.

Como corrigir?

A situação complicada de se aumentar o espaço de memória para a JVM é devido a complexidade do seu funcionamento, no qual o desenvolver precisa ter o conhecimento básico de seu funcionamento, regiões e responsabilidades para assim ter autonomia de parametrização. Para aqueles interessados segue alguns artigos que cobrem esses tópicos:

Em aplicações de pequeno/médio porte, a configuração de 2 parâmetros já são suficiente para estabilizar uma solução em produção:

  • Use o -Xms<>m -Xmx<>m para declara espaço área “HEAP” da JVM – lugar aonde os objetos da aplicação criados pela solução são alocados.
  • Use o -XX:PermSize=<>m -XX:MaxPermSize=<>m para declarar o espaço da área “PERMANENT GENERATION” da JVM – lugar aonde as todas as classes existente da solução são carregadas para execução.

Em soluções de larga escala, muitos parâmetros podem ser alterados, inclusive selecionar e customizar diferentes tipos de coletores de lixo.

“Nós, que somos fortes, devemos suportar as fraquezas dos fracos, e não agradar a nós mesmos.” Romanos 15:1

Anúncios

10 comentários em “A Famosa Falta de Memória OutOfMemoryError

    Orlando disse:
    13/12/2011 às 12:48

    Bacana!
    Onde trabalho tivemos problemas como esse de memória algum tempo atras.
    Na época utilizamos algumas técnicas, dentre elas a configuração dos parametros, paginação e atribuindo null aos objetos como você citou no texto.
    Tivemos um ótimo resultado! Desde então os travamentos não aconteceram mais. =)
    É incrivel como esses detalhes podem fazer muita diferença na aplicação.

    Abraço

    Fernando Franzini respondido:
    13/12/2011 às 12:53

    Sim Orlando. Costumo dizer nas consultorias e paletas que em questões de performance e economia de memória os detalhes fazem a diferença. Ou seja, nos ganhamos na soma dos detalhes.

    Homepage disse:
    14/12/2011 às 02:54

    … [Trackback]…

    […] Informations on that Topic: fernandofranzini.wordpress.com/2011/12/13/a-famosa-falta-de-memoria-java-lang-outofmemoryerror/ […]…

    Lucas Lopes disse:
    16/12/2011 às 10:03

    Excelente artigo, gostaria de saber o motivo do seguinte item:
    “Usar StringBuffer quando manipular algo volume de string.”
    Eu acredito que é melhor utilizar StringBuilder por não ser thread safe o que o torna mais rápido. Se não me engano a kathy sierra também recomenda no seu livro para certificação.
    Outra fonte: http://blog.caelum.com.br/revisitando-a-concatenacao-de-strings-stringbuilder-e-stringbuffer/

      Fernando Franzini respondido:
      16/12/2011 às 10:17

      Sim. Pode ser usado, mas e questão do artigo era gasto de memoria e não performance me processos concorrente em manipulação de string.

    […] Veja nesta série de artigos bem humorados escrita pela Nikita Salniko abordando a situação real de quem um dia se deparou com o famoso OutOfMemoryError: […]

    […] A Famosa Falta de Memória OutOfMemoryError […]

    […] A Famosa Falta de Memória OutOfMemoryError […]

    Débora disse:
    22/08/2015 às 19:09

    Agradeço a sua admirável explicação. Eu gostaria de saber como solucionar esse erro num celular Nokia C3.
    Obrigada.

    […] A Famosa Falta de Memória OutOfMemoryError […]

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s