java.lang.OutOfMemoryError: Java heap space

Bom, postei como monitorar aplicações com o JConsole e como monitorar o Tomcat com o Probe. Até ai tudo bem. Mas e o uso da memória? Como configurar isso? Veremos isso neste post.

Lembro bem de gente dizendo: “O Java? É mega pesado! Um JOptionPane levanta 500 classes e consome 20 mega de memória!“. E ai? Será que isso é verdade?

Bom… nem tudo.

Realmente ele levanta 500 classes pra fazer um JOptionPane… porém o consumo é de 1 mb de memória, contra os 20 da lenda urbana.

Eu juro que se abrir apenas um JOptionPane e ir no consumo de memória da minha máquina vai mostrar 20 mb!!!“.

E vai.

Mas como? Se consome apenas 1 mega, no Sistema Operacional pode estar 20 ?????

A resposta é simples: qualquer aplicação java, a JVM aloca pelo menos 20 mb para ela. E ela pode chegar a, no máximo, 60 mb. Mesmo que nao utilize tudo.

Qual o programador java que nunca teve um java.lang.OutOfMemoryError ?????

Vamos ver como podemos contornar isso… mas para tanto, teremos de entender como funciona o uso de memória nas aplicações Java.

Para a prova SCJP, temos de entender que existem dois tipos de memória no Java: a Stack e a Heap. Mas este conceito é muito abstrato: na realidade, existem mais tipos de memória no Java, sendo que as mesmas possuem sub-seções. Abaixo, cada uma delas:

Stack: É a pilha de execução. Cada método executado é colocado na pilha. Desta forma, o nível mais baixo desta pilha é o inicio de uma Thread (o metodo main é inciado por uma Thread).

Heap: É o local onde ficam os objetos instanciados. É dividida em três partes: a “Eden Space”(ou Young Gen), a “Survivor Space” e a “Tenured Gen”(ou Old Gen).

Permanent Generation: Local onde ficam os dados de reflexão da prórpia máquina virtual, dados de classe e de métodos. É dividida em áreas de leitura/escrita ou somente leitura.

Code Cache: Contém memória para compilar e guardar códigos nativos.

Mais sobre a Heap

Aqui é onde os objetos são instanciados. Logo de cara, todo objeto instanciado vai para a Eden Space. Normalmente, o objeto morre ali mesmo. Quando é efetuado uma Garbage Collection, os objetos da Eden Space que ainda possuem referência são movidos para alguma das Survivor Spaces (existem duas). Os caras que ficarem muito tempo “vivos” na Survivor Space são movidos para a Ternured Space. Por isso a Ternured Space é o maior espaço de memória da Heap.

Vamos fazer um pequeno teste. Olhe o código abaixo:

import java.util.ArrayList;
import java.util.List;

/**
*
* @author hallan
*/
public class Main {

    public static void main(String[] args) {

        List lista = new ArrayList();

        for ( int i = 0; i < 8000000; i++ ){
            lista.add( “TESTES TESTES” );
        }
    }
}

Ele instancia uma lista com “apenas” 8 milhões de Strings. Ao executar:

java Main

Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2760)
at java.util.Arrays.copyOf(Arrays.java:2734)
at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
at java.util.ArrayList.add(ArrayList.java:351)
at testes.Main.main(Main.java:25)

Java Result: 1

Tcham tcham tcham tcham!!! Pelo motivo do erro, podemos perceber que o problema esta na HEAP: a VM praticamente diz: “Ow mané! Ta faltando Heap Space aqui pra isso pow!”

Vamos executar novamente, agora desta forma:

java -Xmx128m Main

Não deu problema! Mas… o que que houve? Simples: passamos para a JVM que, para esta execução, alocaremos NO MÁXIMO 128 mb ao invés do 64 mb default que ela permite usar. Outro parâmetro interessante é o Xmx: ele define a memória mínima para a execução. Desta forma:

java -Xms64m -Xmx128m Main

Com o comando acima, definimos de 64 mb de memória serão alocados diretamente para esta aplicação, mesmo que a mesma utilize apenas 1 mb. E caso 64 mb não sejam suficientes, ela pode usar ATÉ 128 mb. Caso ultrapasse os 128 mb, irá lançar um gostoso “java.lang.OutOfMemoryError: Java heap space”. Hehehe!

Ta… e o meu Eclipse? Onde altero isso?
No diretório onde o Eclipse está instalado, tem um arquivo chamado eclipse.ini, que contém os parâmetros da JVM. Caso esteja vazio ou não contenha estes parâmetros, basta adicionar (exemplo abaixo):
-vmargs
-Xms128m
-Xmx512m

E no NetBeans???
No diretório de instalação do NetBeans, entre em /etc e abra o arquivo netbeans.conf:
netbeans_default_options=”-J-Xms128m -J-Xmx512m”

E no JEdit?
O JEdit normalmente não precisa de muita memória… mas as vezes, ao trabalhar com um arquivo muito grande, pode-se ter problemas. O JEdit é na mão grande mesmo: não tem arquivo de configuração:
Da pra alterar o atalho da área de trabalho:
$JDK_HOMEbinjavaw.exe -Xmx128m -Xms256m -jar “$JEDIT_HOMEjedit.jar”
onde JDK_HOME é o diretorio do JDK e o JEDIT_HOME é o diretório do JEDIT
ou pela linha de comando mesmo:
java -Xmx128m -Xms128m -jar jedit.jar

E o meu Tomcat??
Dentro do catalina.sh (ou catalina.bat para windows) basta criar a variável CATALINA_OPTS ou JAVA_OPTS e adicionar os parâmetros:
CATALINA_OPTS=”-Xms256m -Xmx512m” ou
JAVA_OPTS=”-Xms256m -Xmx512m”

Bom… espero ter ajudado! No próximo post falarei sobre a Permanent Generation e a Stack.

Abraço!

Deixe um Comentário

4 Comentários.

  1. Meo deus tais muito fera.. muito loco. hehehe

    o/

  2. Sou programador Java a 3 anos e nunca havia visto um post tão interessante sobre
    como o Java trata a memória. Parabéns.

  3. Obrigado Felipe! Acredito que entender como o Java trata a memória é essencial para que possamos desenvolver de forma melhor, além de poder tratar os erros de memória do Eclipse/Tomcat/Jboss de forma eficiente.

    Abraço!

  4. Cara como faço para alterar o meu diz que não tenho permissão.

Deixe um Comentário


NOTA - Você pode usar estesHTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>