Como lidar com o erro "java.lang.OutOfMemoryError: Java heap space"?

416

Estou escrevendo um aplicativo Swing do lado do cliente (designer de fonte gráfica) em Java 5 . Recentemente, estou tendo java.lang.OutOfMemoryError: Java heap spaceerro porque não estou sendo conservador quanto ao uso de memória. O usuário pode abrir um número ilimitado de arquivos, e o programa mantém os objetos abertos na memória. Após uma rápida pesquisa, encontrei Ergonomia na Java Virtual Machine 5.0 e outros disseram na máquina Windows que a JVM padroniza o tamanho máximo de heap como 64MB.

Diante dessa situação, como devo lidar com essa restrição?

Eu poderia aumentar o tamanho máximo do heap usando a opção de linha de comando para java, mas isso exigiria descobrir a RAM disponível e escrever algum programa ou script de inicialização. Além disso, aumentar para um máximo finito não acaba com o problema.

Eu poderia reescrever parte do meu código para persistir objetos no sistema de arquivos com freqüência (usar o banco de dados é a mesma coisa) para liberar a memória. Pode funcionar, mas provavelmente também é muito trabalhoso.

Se você puder me indicar detalhes das idéias acima ou algumas alternativas, como memória virtual automática, estender o tamanho da pilha dinamicamente , isso será ótimo.

Eugene Yokota
fonte
O tamanho máximo padrão do heap de 64 MB é anterior ao J2SE 5.0. Para obter informações sobre o J2SE 8.0, consulte "Ergonomia do coletor de lixo" em docs.oracle.com/javase/8/docs/technotes/guides/vm/… .
Andy Thomas
Se você chegou aqui porque todas as perguntas da OOM são enganadas para essa, verifique também: stackoverflow.com/questions/299659/… Ele fornece a solução para limpar as referências de memória 'just in time' antes da OOM. SoftReferences pode ser a ferramenta que resolve o seu problema real.
21318 Steve Steiner

Respostas:

244

Por fim, você sempre tem um máximo finito de heap para usar, independentemente da plataforma em que está executando. No Windows 32 bits, isso ocorre em torno de2GB (não especificamente na pilha, mas na quantidade total de memória por processo). Ocorre que o Java escolhe diminuir o padrão (presumivelmente para que o programador não possa criar programas que tenham alocação de memória descontrolada sem enfrentar esse problema e ter que examinar exatamente o que está fazendo).

Sendo assim, existem várias abordagens que você pode adotar para determinar a quantidade de memória necessária ou reduzir a quantidade de memória que está usando. Um erro comum nas linguagens de coleta de lixo, como Java ou C #, é manter referências de objetos que você não está mais usando ou alocar muitos objetos quando você pode reutilizá- los. Desde que os objetos tenham uma referência a eles, eles continuarão a usar o espaço de heap, pois o coletor de lixo não os excluirá.

Nesse caso, você pode usar um criador de perfil de memória Java para determinar quais métodos em seu programa estão alocando um grande número de objetos e, em seguida, determinar se existe uma maneira de garantir que eles não sejam mais referenciados ou para não alocá-los em primeiro lugar. Uma opção que eu usei no passado é "JMP" http://www.khelekore.org/jmp/ .

Se você determinar que está alocando esses objetos por um motivo e precisar manter as referências (dependendo do que estiver fazendo, pode ser o caso), precisará aumentar o tamanho máximo do heap ao iniciar o programa. No entanto, depois de fazer o perfil da memória e entender como seus objetos estão sendo alocados, você deve ter uma idéia melhor sobre a quantidade de memória necessária.

Em geral, se você não pode garantir que seu programa será executado em uma quantidade finita de memória (talvez dependendo do tamanho da entrada), você sempre encontrará esse problema. Somente depois de esgotar tudo isso, você precisará examinar os objetos em cache no disco, etc. Nesse momento, você deve ter um bom motivo para dizer "Preciso de Xgb de memória" para algo e não conseguirá solucionar isso melhorando seus algoritmos ou padrões de alocação de memória. Geralmente, isso geralmente acontece apenas com algoritmos que operam em grandes conjuntos de dados (como um banco de dados ou algum programa de análise científica) e, em seguida, técnicas como cache e E / S mapeadas na memória se tornam úteis.

Ben Childs
fonte
6
O OpenJDK e o OracleJDK empacotaram o profiler - jvisualvm. Se você quiser mais conveniências, sugiro Yourkit comercial.
precisa saber é o seguinte
121

Execute o Java com a opção de linha de comando -Xmx, que define o tamanho máximo da pilha.

Veja aqui para detalhes .

Dave Webb
fonte
3
Como definir esse parâmetro para sempre? Porque estou usando o comando 'gradlew assemble'.
precisa saber é o seguinte
2
Run-> Run Configurations-> Clique na arguments-> dentro argumentos VM digite -Xms1g -Xmx2g
Arayan Singh
2
Essa é a verdadeira resposta.
NCCC
85

Você pode especificar por projeto quanto espaço de heap seu projeto deseja

A seguir, é apresentado o Eclipse Helios / Juno / Kepler :

Clique com o botão direito do mouse em

 Run As - Run Configuration - Arguments - Vm Arguments, 

então adicione isso

-Xmx2048m
allenhwkim
fonte
1
oi bighostkim e cuongHuyTo, onde está "Arguments" .. eu posso ver até a Configuração de Execução. Por favor, me fale. Minha necessidade de baixar e armazenar quase 2.000 contatos do gmail. Ele falhar devido a fora de exceção de memória
AndroidRaji
@AndroiRaji: você clica com o botão direito do mouse na classe Java que possui um main executável (que é "public static void main (String [] args)") e, em seguida, escolhe Executar como - Executar configuração. Então "Arguments" é a guia logo após a Main (você vê as guias Main, Arguments, JRE, Classpath, Source, Environment, Common).
precisa saber é o seguinte
47

Aumentar o tamanho do heap não é uma "correção", é um "gesso", 100% temporário. Ele irá travar novamente em outro lugar. Para evitar esses problemas, escreva código de alto desempenho.

  1. Use variáveis ​​locais sempre que possível.
  2. Certifique-se de selecionar o objeto correto (EX: Seleção entre String, StringBuffer e StringBuilder)
  3. Use um bom sistema de código para o seu programa (EX: Usando variáveis ​​estáticas VS variáveis ​​não estáticas)
  4. Outras coisas que podem funcionar no seu código.
  5. Tente mover-se com multy THREADING
Suco de limão
fonte
Isto é tão verdade. Estou tentando corrigir um problema em que estou obtendo OOM no encadeamento AWT, mas se eu usar outro encadeamento diferente, não estou obtendo problema no OOM. Tudo o que posso encontrar on-line é aumentar o tamanho da pilha do encadeamento AWT.
Ashish
@ Ash: Sim, corrija o problema principal em vez de procurar emplastros.
Sumo de Limão
A coleta de lixo e a abordagem de gerenciamento de memória em Java deveriam resolver todas essas complicações malloc-dealloc de seus antecessores :( É claro que concordo completamente com esta resposta. É uma pena que os padrões não facilitem a escrita de código com dados enxutos -construções e suas partes que são limpas o mais rápido possível.
Davos
31

Uma grande ressalva ---- no meu escritório, estávamos descobrindo que (em algumas máquinas Windows) não era possível alocar mais de 512m para o heap Java. Isso aconteceu devido ao produto antivírus Kaspersky instalado em algumas dessas máquinas. Depois de desinstalar o produto AV, descobrimos que poderíamos alocar pelo menos 1,6 GB, ou seja, -Xmx1600m(m é obrigatório, caso contrário, isso levará a outro erro "Heap inicial muito pequeno").

Não faço ideia se isso acontece com outros produtos AV, mas presumivelmente isso está acontecendo porque o programa AV está reservando um pequeno bloco de memória em todos os espaços de endereço, impedindo, assim, uma única alocação muito grande.

David
fonte
22

Os argumentos da VM funcionaram para mim no eclipse. Se você estiver usando o eclipse versão 3.4, faça o seguinte

vá para Run --> Run Configurations -->selecionar o projeto em maven build -> selecione a guia "JRE" -> e digite -Xmx1024m.

Como alternativa, você pode Run --> Run Configurations --> select the "JRE" tab -->digitar -Xmx1024m

Isso deve aumentar a pilha de memória para todas as compilações / projetos. O tamanho da memória acima é de 1 GB. Você pode otimizar da maneira que desejar.

amo tudo
fonte
18

Sim, -Xmxvocê pode configurar mais memória para sua JVM. Para garantir que você não vaze ou desperdice memória. Faça um despejo de pilha e use o Eclipse Memory Analyzer para analisar seu consumo de memória.

kohlerm
fonte
JVMJ9VM007E Opção da linha de comandos não reconhecida: -Xmx Não foi possível criar a máquina virtual Java. Downvote
Philip Rego
17

Gostaria de adicionar recomendações da resolução de problemas da oracle artigo sobre .

Exceção no encadeamento thread_name: java.lang.OutOfMemoryError: espaço de heap Java

A mensagem de detalhe Espaço de heap Java indica que o objeto não pôde ser alocado no heap Java. Este erro não implica necessariamente um vazamento de memória

Causas Possíveis:

  1. Problema de configuração simples , em que o tamanho de heap especificado é insuficiente para o aplicativo.

  2. O aplicativo não intencionalmente mantém referências a objetos , e isso evita que os objetos sejam coletados no lixo.

  3. Uso excessivo de finalizadores .

Uma outra fonte potencial desse erro surge com aplicativos que fazem uso excessivo de finalizadores. Se uma classe tiver um método finalize, os objetos desse tipo não terão seu espaço recuperado no momento da coleta de lixo

Após a coleta de lixo , os objetos são enfileirados para finalização , o que ocorre posteriormente. os finalizadores são executados por um encadeamento daemon que atende à fila de finalização. Se o encadeamento do finalizador não puder acompanhar a fila de finalização, o heap Java poderá ser preenchido e esse tipo de exceção OutOfMemoryError será acionado.

Um cenário que pode causar essa situação é quando um aplicativo cria encadeamentos de alta prioridade que fazem com que a fila de finalização aumente a uma taxa mais rápida que a taxa na qual o encadeamento do finalizador está atendendo a essa fila.

Ravindra babu
fonte
9

Siga as etapas abaixo:

  1. Aberto catalina.shdo tomcat / bin.

  2. Altere JAVA_OPTS para

    JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1536m 
    -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m 
    -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
  3. Reinicie seu tomcat

Pradip Bhatt
fonte
8

Eu li em outro lugar que você pode tentar - pegar java.lang.OutOfMemoryError e no bloco catch, você pode liberar todos os recursos que você sabe que podem usar muita memória, fechar conexões e assim por diante. System.gc() depois tentar novamente o que quer que seja você ia fazer.

Outra maneira é essa, embora eu não saiba se isso funcionaria, mas atualmente estou testando se funcionará no meu aplicativo.

A idéia é fazer a coleta de lixo chamando System.gc (), conhecido por aumentar a memória livre. Você pode continuar verificando isso após a execução de um código de consumo de memória.

//Mimimum acceptable free memory you think your app needs
long minRunningMemory = (1024*1024);

Runtime runtime = Runtime.getRuntime();

if(runtime.freeMemory()<minRunningMemory)
 System.gc();
mwangi
fonte
6
Em geral, acho que a JVM preferirá Garbage Collect (GC) do que lançar um OutOfMemoryError. Chamar explicitamente System.gc () após OutOfMemoryError pode ajudar em algumas VMs / configurações, mas eu não esperaria que funcionasse muito bem no caso geral. No entanto, descartar referências de objetos desnecessárias definitivamente ajudaria em quase todos os casos.
Mike Clark
6
@mwangi Chamar System.gc () diretamente do código geralmente é uma má idéia. É apenas uma sugestão para a JVM de que o GC deva ser executado, mas não há absolutamente nenhuma garantia de que ele será executado.
7

A maneira mais fácil de resolver OutOfMemoryErrorem java é aumentar o tamanho máximo de heap usando as opções da JVM -Xmx512M; isso resolverá imediatamente seu OutOfMemoryError. Esta é a minha solução preferida quando obtenho o OutOfMemoryError no Eclipse, Maven ou ANT durante a criação do projeto, porque com base no tamanho do projeto, você pode facilmente ficar sem memória.

Aqui está um exemplo de aumento do tamanho máximo de heap da JVM. Também é melhor manter a proporção de -Xmx para -Xms 1: 1 ou 1: 1,5 se você estiver configurando o tamanho de heap no aplicativo java.

export JVM_ARGS="-Xms1024m -Xmx1024m"

Link de referência

chaukssey
fonte
1
Alguma idéia de por que precisamos mantê-los na proporção de 1: 1 ou 1: 1,5?
ernesto
7

Por padrão para desenvolvimento, a JVM usa tamanho pequeno e configuração pequena para outros recursos relacionados ao desempenho. Mas para produção, você pode ajustar, por exemplo (além disso, pode existir uma configuração específica do servidor de aplicativos) -> (se ainda não houver memória suficiente para atender à solicitação e o heap já tiver atingido o tamanho máximo, ocorrerá um OutOfMemoryError)

-Xms<size>        set initial Java heap size
-Xmx<size>        set maximum Java heap size
-Xss<size>        set java thread stack size

-XX:ParallelGCThreads=8
-XX:+CMSClassUnloadingEnabled
-XX:InitiatingHeapOccupancyPercent=70
-XX:+UnlockDiagnosticVMOptions
-XX:+UseConcMarkSweepGC
-Xms512m
-Xmx8192m
-XX:MaxPermSize=256m (in java 8 optional)

Por exemplo: No linux Platform, configurações preferenciais do modo de produção.

Depois de baixar e configurar o servidor dessa maneira, http://www.ehowstuff.com/how-to-install-and-setup-apache-tomcat-8-on-centos-7-1-rhel-7/

1.crie o arquivo setenv.sh na pasta / opt / tomcat / bin /

   touch /opt/tomcat/bin/setenv.sh

2.Abra e escreva esses parâmetros para definir o modo preferencial.

nano  /opt/tomcat/bin/setenv.sh 

export CATALINA_OPTS="$CATALINA_OPTS -XX:ParallelGCThreads=8"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+CMSClassUnloadingEnabled"
export CATALINA_OPTS="$CATALINA_OPTS -XX:InitiatingHeapOccupancyPercent=70"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UnlockDiagnosticVMOptions"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseConcMarkSweepGC"
export CATALINA_OPTS="$CATALINA_OPTS -Xms512m"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx8192m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=256M"

3)service tomcat restart

Observe que a JVM usa mais memória do que apenas a pilha. Por exemplo, métodos Java, pilhas de encadeamentos e identificadores nativos são alocados na memória separada do heap, bem como nas estruturas de dados internas da JVM.

musa
fonte
7

Eu enfrentei o mesmo problema do tamanho da pilha de java.

Eu tenho duas soluções, se você estiver usando o java 5 (1.5).

  1. basta instalar o jdk1.6 e acessar as preferências do eclipse e definir o caminho do jre do jav1 1.6 como você instalou.

  2. Verifique o argumento da sua VM e deixe que seja o que for. basta adicionar uma linha abaixo de todos os argumentos presentes nos argumentos da VM como -Xms512m -Xmx512m -XX: MaxPermSize = ... m (192m).

Eu acho que vai funcionar ...

Soumya Sandeep Mohanty
fonte
7

Se você precisar monitorar o uso da memória em tempo de execução, o java.lang.managementpacote ofereceMBeans que pode ser usado para monitorar os conjuntos de memórias na sua VM (por exemplo, espaço eden, geração protegida etc.) e também o comportamento da coleta de lixo.

O espaço livre de heap relatado por esses MBeans variará bastante, dependendo do comportamento do GC, principalmente se o aplicativo gerar muitos objetos posteriormente editados pelo GC. Uma abordagem possível é monitorar o espaço de heap livre após cada GC completo, que você pode usar para tomar uma decisão sobre a liberação de memória persistindo por objetos.

Por fim, sua melhor aposta é limitar a retenção de memória o máximo possível, enquanto o desempenho permanece aceitável. Como um comentário anterior observou, a memória é sempre limitada, mas seu aplicativo deve ter uma estratégia para lidar com o esgotamento da memória.

Leigh
fonte
5

Observe que, se você precisar disso em uma situação de implementação, considere usar o Java WebStart (com uma versão "ondisk", não a rede - possível no Java 6u10 e posterior), pois permite especificar os vários argumentos para a JVM em uma cruz caminho da plataforma.

Caso contrário, você precisará de um iniciador específico do sistema operacional que defina os argumentos necessários.

Thorbjørn Ravn Andersen
fonte
O Java WebStart está sendo desativado. Ainda não estou ciente de uma substituição adequada.
Thorbjørn Ravn Andersen
1

Se esse problema estiver ocorrendo no Wildfly 8 e no JDK1.8, precisamos especificar as configurações do MaxMetaSpace em vez das configurações do PermGen.

Por exemplo, precisamos adicionar a configuração abaixo no arquivo setenv.sh do wildfly. JAVA_OPTS="$JAVA_OPTS -XX:MaxMetaspaceSize=256M"

Para mais informações, consulte Wildfly Heap Issue

satish
fonte
1

Em relação ao netbeans, você pode definir o tamanho máximo de heap para resolver o problema.

Vá para 'Executar', depois -> 'Definir configuração do projeto' -> 'Personalizar' -> 'executar' da janela exibida -> 'Opção da VM' -> preencha '-Xms2048m -Xmx2048m' .

Xiaogang
fonte
1

Se você continuar alocando e mantendo referências a objetos, preencherá toda a quantidade de memória que tiver.

Uma opção é fechar e abrir um arquivo transparente quando eles alternam as guias (você mantém apenas um ponteiro para o arquivo, e quando o usuário alterna a guia, você fecha e limpa todos os objetos ... isso fará com que o arquivo mude mais lentamente ... mas ...) e talvez mantenha apenas 3 ou 4 arquivos na memória.

Outra coisa que você deve fazer é, quando o usuário abrir um arquivo, carregá-lo e interceptar qualquer OutOfMemoryError, então (como não é possível abrir o arquivo) feche esse arquivo, limpe seus objetos e avise o usuário que ele deve fechar sem uso arquivos.

Sua idéia de estender dinamicamente a memória virtual não resolve o problema, pois a máquina é limitada em recursos; portanto, você deve ter cuidado e lidar com problemas de memória (ou pelo menos, ter cuidado com eles).

Algumas dicas que eu vi com vazamentos de memória são:

-> Lembre-se de que se você colocar algo em uma coleção e depois esquecê-la, você ainda terá uma forte referência a ela; portanto, anule a coleção, limpe-a ou faça algo com ela ... caso contrário, você encontrará um vazamento de memória difícil de encontrar.

-> Talvez, o uso de coleções com referências fracas (fracohashmap ...) possa ajudar com problemas de memória, mas você deve ter cuidado com isso, pois pode achar que o objeto que procura foi coletado.

-> Outra idéia que encontrei é desenvolver uma coleção persistente que é armazenada nos objetos de banco de dados menos usados ​​e carregados de forma transparente. Essa provavelmente seria a melhor abordagem ...

SoulWanderer
fonte
0

Se tudo mais falhar, além de aumentar o tamanho máximo da pilha, tente também aumentar o tamanho da troca. Para Linux, a partir de agora, instruções relevantes podem ser encontradas em https://linuxize.com/post/create-a-linux-swap-file/ .

Isso pode ajudar se você estiver, por exemplo, compilando algo grande em uma plataforma incorporada.

nccc
fonte