AFF Education Construa seu Futuro

☕ Java para Iniciantes

Aula 62  |  Uso de Memória em Java

Como o Java usa a Memória?

A memória RAM (Random Access Memory, ou Memória de Acesso Aleatório) é um componente físico do computador que armazena temporariamente os dados e instruções que estão sendo usados naquele momento. Diferente do disco rígido (HD ou SSD), que guarda informações de forma permanente, a RAM é rápida e volátil — ou seja, tudo que está nela é apagado quando o programa termina ou o computador é desligado.

Quando um programa Java é executado, o computador reserva espaços na sua memória RAM para guardar cada variável, objeto e constante. Cada espaço reservado recebe um endereço de memória — um número que identifica exatamente onde aquela informação está armazenada.

Endereço de memória é o número (normalmente exibido em hexadecimal) que indica a posição exata na RAM onde um dado está guardado. Em Java, quando criamos um objeto com new, a variável-referência guarda o endereço desse objeto, não o objeto em si.

Para entender o conceito de endereço de memória, pense em uma rua com várias casas. Cada casa tem um número que a identifica de forma única: 125, 129, 133, 137, 141… Quando você quer encontrar alguém, não precisa carregar a casa inteira — basta saber o número dela. Com esse número, você chega até a casa e encontra o que há lá dentro: os móveis, as pessoas, os objetos.

Rua com casas numeradas (125, 129, 133, 137, 141) representando endereços de memória

Cada casa da rua tem um número único, assim como cada compartimento da memória tem um endereço único.

Na memória do computador é exatamente igual: cada compartimento de memória tem um endereço único (como o número da casa). O programa usa esse endereço para localizar onde uma informação está guardada. Essa analogia será útil ao longo de toda a aula — guarde-a bem:

🏠 Número da casa = endereço do compartimento de memória.

📦 O que há dentro da casa = o conteúdo (dado/valor) guardado naquele endereço.

O Java divide a memória em regiões. As três principais que aparecem no exemplo desta aula são:

📚 Stack (Pilha)

Armazena variáveis locais e referências a objetos. Na nossa analogia, o Stack guarda apenas o número da casa (o endereço) — não o conteúdo dela.

🏗️ Heap

Armazena os objetos criados com new. Na analogia, o Heap é a casa em si com tudo dentro — o endereço e todo o conteúdo (os atributos do objeto). O Stack guarda apenas o número que aponta para essa casa.

📌 Área Estática

Armazena variáveis e constantes declaradas como static. Existem durante toda a execução do programa, independentemente de objetos.

O Código do Exemplo

Vamos usar o mesmo programa da Parte 1 para entender onde cada variável e objeto ficam guardados na memória.

public class PesoAltura {
    int peso;   // peso em quilogramas
    int altura; // altura em centímetros
}

public class EstruturaSimples {
    public static final int alturaMaxima = 225; // constante estática

    public static void main(String[] args) {
        PesoAltura pessoa1 = new PesoAltura(); // cria objeto no Heap
        pessoa1.peso   = 80;
        pessoa1.altura = 185;

        System.out.print("Peso: " + pessoa1.peso + ", Altura " + pessoa1.altura + ". ");

        if (pessoa1.altura > alturaMaxima)
            System.out.println("Altura acima da máxima.");
        else
            System.out.println("Altura abaixo da máxima.");
    }
}

Saída no terminal:

Peso: 80, Altura 185. Altura abaixo da máxima.

Diagrama de Memória do Programa

O diagrama abaixo ilustra o que acontece na memória quando o programa é executado. Os números em vermelho (ex.: 9742, 3408) representam endereços de memória fictícios usados para mostrar como as referências funcionam.

Área Estática
alturaMaxima
225
endereço
9742
Stack (Pilha)
args
9742
pessoa1
3408 →
➜
Heap (Objeto)
endereço
3408
peso
80
altura
185
Lendo o diagrama: a variável pessoa1 está no Stack e guarda o endereço 3408. Esse endereço aponta para o local no Heap onde o objeto PesoAltura de fato existe, com seus atributos peso = 80 e altura = 185. A constante alturaMaxima fica na área estática, com endereço 9742.
🏠 Voltando à analogia da rua: o Stack é como uma agenda que anota apenas o número da casa (3408) — leve e rápida de consultar. O Heap é a casa de número 3408 com tudo dentro: o peso, a altura e os demais atributos do objeto. Para saber o que há na casa, o Java primeiro lê o número na agenda (Stack) e depois vai até a casa (Heap).

Passo a Passo na Memória

Clique em cada passo para ver o que acontece na memória durante a execução:

public static final int alturaMaxima = 225;

A constante alturaMaxima é carregada na área estática da memória com o valor 225. Como é static, ela existe desde o início da execução, antes mesmo de qualquer objeto ser criado. Seu endereço fictício é 9742.

Área Estática
alturaMaxima
225
endereço
9742
public static void main(String[] args) { ... }

O método main é chamado. O parâmetro args é criado no Stack. Como é um objeto array, ele guarda um endereço de memória (fictício 9742) que aponta para o array real no Heap.

Stack
args
9742
PesoAltura pessoa1 = new PesoAltura();

O new PesoAltura() cria um objeto no Heap no endereço 3408. A variável pessoa1 é criada no Stack e recebe esse endereço (3408) como valor. Por isso dizemos que pessoa1 é uma referência ao objeto.

Stack
args
9742
pessoa1
3408 →
➜
Heap (novo objeto)
endereço
3408
peso
0 (padrão)
altura
0 (padrão)
pessoa1.peso = 80;  pessoa1.altura = 185;

Usando o operador ponto (.), o Java segue o endereço guardado em pessoa1 (que é 3408) até o objeto no Heap e atribui os valores 80 e 185 aos atributos peso e altura daquele objeto.

Stack
pessoa1
3408 →
➜
Heap (atualizado)
peso
80 ✅
altura
185 ✅
if (pessoa1.altura > alturaMaxima) ...

O Java busca pessoa1.altura no Heap (endereço 3408) e encontra 185. Em seguida busca alturaMaxima na área estática (endereço 9742) e encontra 225. Como 185 < 225, a condição é false e o bloco else é executado, imprimindo a mensagem.

Peso: 80, Altura 185. Altura abaixo da máxima.

Variável Primitiva × Variável de Referência

Existe uma diferença fundamental entre guardar um valor primitivo e guardar uma referência a um objeto:

Tipo Exemplos O que é guardado no Stack Onde fica o dado real
Primitivo int, double, boolean, char O valor diretamente (ex.: 80) No próprio Stack
Referência String, qualquer classe (PesoAltura, etc.) Um endereço que aponta para o objeto (ex.: 3408) No Heap
Quando você faz int x = 10;, o valor 10 fica diretamente na variável x. Mas quando faz PesoAltura pessoa1 = new PesoAltura();, a variável pessoa1 guarda apenas o endereço do objeto, não o objeto em si!

Comparando na memória: dois exemplos lado a lado

Veja a diferença concreta entre os dois tipos. Considere o código:

int idade = 30;                         // variável PRIMITIVA
PesoAltura pessoa1 = new PesoAltura();  // variável de REFERÊNCIA
pessoa1.peso   = 80;
pessoa1.altura = 185;

① Variável Primitiva — int idade = 30;

O valor fica diretamente no Stack. Não há Heap envolvido — é como ter o dado escrito na própria agenda, sem precisar ir a casa nenhuma.

Stack (Pilha)
idade
30
🏠 Na analogia: o valor 30 está anotado direto na agenda (Stack). Não existe "casa" separada — o conteúdo é o próprio número guardado ali.

② Variável de Referência — PesoAltura pessoa1 = new PesoAltura();

A variável guarda no Stack apenas o endereço (ex.: 3408). O objeto real, com seus atributos, fica no Heap. O Stack aponta para o Heap.

Stack (Pilha)
pessoa1
3408 →
➜
Heap (Objeto)
endereço
3408
peso
80
altura
185
🏠 Na analogia: a agenda (Stack) anota apenas o número da casa (3408). A casa de número 3408 (Heap) é que contém de fato o peso = 80 e a altura = 185. Para ler um atributo, o Java segue o número até a casa.

Resumindo a diferença:

🔴 Primitivo (idade): o Stack guarda o próprio valor (30). Não há endereço apontando para lugar nenhum.

🟢 Referência (pessoa1): o Stack guarda um endereço (3408) e o conteúdo real (peso, altura) fica no Heap, naquele endereço.

Resumo da Aula

✅ A memória RAM é dividida em regiões: Stack, Heap e Área Estática.

✅ Cada variável e objeto ocupa um endereço de memória único.

✅ Variáveis de tipos primitivos (int, double…) guardam o valor diretamente no Stack.

✅ Variáveis de referência (objetos) guardam no Stack o endereço que aponta para o objeto no Heap.

✅ Constantes static final ficam na área estática e existem durante toda a execução.

✅ O operador ponto (.) faz o Java seguir o endereço guardado na referência até o objeto real no Heap.

🧪 Lista de Exercícios

1. Onde fica armazenado o objeto criado com new PesoAltura()?

2. O que a variável pessoa1 guarda no Stack quando fazemos PesoAltura pessoa1 = new PesoAltura();?

3. Onde fica armazenada a constante public static final int alturaMaxima = 225;?

4. O que o operador ponto (.) faz quando escrevemos pessoa1.peso?

5. Se pessoa1.altura = 185 e alturaMaxima = 225, qual bloco do if-else é executado?

6. Qual é a diferença entre um tipo primitivo (int x = 10) e uma variável de referência (PesoAltura p = new PesoAltura()) no que diz respeito ao Stack?

🏆 Desafio

Crie uma classe chamada Carro com os atributos String modelo, int ano e double preco. Na classe principal, declare uma constante static final int anoMinimo = 2000. Crie dois objetos da classe Carro, preencha seus atributos e use um if-else para verificar se o ano de cada carro é maior ou igual a anoMinimo, imprimindo uma mensagem adequada para cada caso.

public class Carro {
    String modelo;
    int    ano;
    double preco;
}

public class DesafioCarro {
    public static final int anoMinimo = 2000;

    public static void main(String[] args) {
        Carro c1 = new Carro();
        c1.modelo = "Gol";
        c1.ano    = 1998;
        c1.preco  = 15000.0;

        Carro c2 = new Carro();
        c2.modelo = "HB20";
        c2.ano    = 2021;
        c2.preco  = 72000.0;

        System.out.print("Carro: " + c1.modelo + " | Ano: " + c1.ano + " -> ");
        if (c1.ano >= anoMinimo)
            System.out.println("Ano aceito.");
        else
            System.out.println("Ano abaixo do mínimo.");

        System.out.print("Carro: " + c2.modelo + " | Ano: " + c2.ano + " -> ");
        if (c2.ano >= anoMinimo)
            System.out.println("Ano aceito.");
        else
            System.out.println("Ano abaixo do mínimo.");
    }
}

Saída esperada:

Carro: Gol | Ano: 1998 -> Ano abaixo do mínimo.

Carro: HB20 | Ano: 2021 -> Ano aceito.

Exercícios de Aplicação AFF Education

Reforce o conteúdo desta aula com mais dois exercícios práticos:

1. Tipos primitivos (ex.: int) costumam ser armazenados:

2. Quando duas variáveis de objeto apontam para a mesma referência, alterar o objeto por uma delas: