Quando um programa está em execução, os dados precisam ser armazenados na memória. O espaço ocupado e o local de armazenamento de cada dado são definidos pelo seu tipo. Durante a execução, duas áreas de memória são utilizadas para guardar informações: Pilha (Stack) e Heap (Monte).
Pilha (Stack)
A pilha é um bloco contínuo de memória que segue o princípio de estrutura de dados Último a entrar, primeiro a sair (LIFO). Ela armazena três categorias de dados:
- Valores de variáveis de determinados tipos
- Contexto atual de execução do programa
- Parâmetros enviados para os métodos

Todas as operações de memória da pilha são gerenciadas automaticamente pelo sistema, não há necessidade de manipulação manual por parte do desenvolvedor. Compreender a lógica interna da pilha simplifica a análise do fluxo de execução e a leitura da documentação oficial do C#.
Características fundamentais da pilha
- Só é possível adicionar e remover dados no topo da pilha
- Adicionar dados no topo: Push (empilhar)
- Retirar dados do topo: Pop (desempilhar)
Propriedades da memória de pilha
- Alocação e liberação de memória extremamente rápidas, utiliza blocos sequenciais de RAM
- Ciclo de vida vinculado à chamada de método: o quadro de pilha é destruído automaticamente após o término da execução do método
- Armazena: variáveis de tipo valor, parâmetros de método, variáveis locais, contexto de execução

Para entender melhor o funcionamento do heap, vamos analisar um exemplo de código simples
class Program
{
static void Main(string[] args)
{
int a = 10;
int b = 20;
int sum = Add(a, b);
}
static int Add(int x, int y)
{
return x + y;
}
}
Code language: JavaScript (javascript)
A lógica deste exemplo é muito simples. A partir da primeira linha da função Main — ponto de entrada do programa — criamos uma variável inteira a, depois a variável b na segunda linha. A terceira linha invoca a função Add, passando os valores de a e b para dentro do método. A variável x recebe o valor de a, y recebe o valor de b, e a função retorna o resultado da soma x + y igual a 30 para o ponto de chamada, onde o valor é atribuído à variável sum. Quando a função Add concluir sua execução, x e y são desempilhados, sum passa a valer 30 e o programa é finalizado após o término de Main. Abaixo, detalhamos passo a passo todo o processo de Push e Pop na pilha.
Fluxo de execução do programa e processo Push/Pop da pilha





Ao finalizar Add, x e y são parâmetros locais desta função, sendo desempilhados um após o outro para liberar espaço e garantir o funcionamento fluido do sistema.
Após o Pop, x e y deixam de existir, e o valor 30 retornado por Add é enviado ao ponto de chamada para ser armazenado em sum.




Lembre-se de um ponto essencial: durante a execução de uma função, as variáveis são criadas de cima para baixo e empilhadas na sequência a → b → entrada em Add → x → y. Ao terminar Add, x e y são desempilhados, depois sum é empilhado; quando Main encerra, as variáveis são removidas na ordem inversa da criação: sum, b, a.
Heap (Monte)
O heap é uma ampla área de memória destinada à alocação dinâmica de blocos para armazenar objetos de dados. Diferente da pilha, os dados do monte podem ser adicionados e removidos em qualquer ordem.
O programa não consegue excluir manualmente os objetos do heap; o Coletor de Lixo (GC) do CLR (Common Language Runtime) limpa automaticamente os objetos órfãos sem referências, que ficam inacessíveis. Isso evita erros causados pela liberação manual de memória, obrigatória em outras linguagens.
O heap pode ser comparado a uma mesa ampla, onde podemos colocar objetos livremente. Já a pilha, como explicado anteriormente, se assemelha a um tubo de moedas com apenas uma extremidade de acesso.

Fluxo de coleta do GC
- O programa cria três objetos no heap e mantém referências válidas para eles
- O programa deixa de utilizar um dos objetos, todas as referências a ele desaparecem
- O GC detecta o objeto órfão sem referências e libera a memória que ele ocupa
- Coleta concluída, a memória liberada pode ser alocada para novos objetos
Propriedades da memória do heap
- Espaços de memória descontínuos, velocidade de alocação inferior à da pilha
- Ciclo de vida não limitado pelos limites do método, gerenciado de forma centralizada pelo GC
- Armazena: instâncias de tipos de referência (classes, vetores, delegados etc.); na pilha só são guardados os endereços de memória dos objetos do heap
Vamos analisar um exemplo prático para tornar o conceito mais claro. Como mencionado antes, variáveis locais de tipo primitivo são armazenadas na pilha, enquanto tipos definidos pelo usuário geralmente residem no heap. Confira o código abaixo:
class Program
{
static void Main(string[] args)
{
int a = 10;
int b = 20;
Student tom = new Student("Tom", 12);
Student lucy = new Student("Lucy", 12);
}
}
class Student
{
//Variáveis de membro
private string name;
private int age;
//Construtor, explicado mais adiante
public Student(string name, int age)
{
this.name = name;
this.age = age;
}
//Método de membro
public void DisplayInfo()
{
Console.WriteLine($"Name: {name}, Age: {age}");
}
}
Code language: JavaScript (javascript)
A ilustração abaixo mostra a estrutura de armazenamento em memória das quatro variáveis a, b, tom e lucy dentro de Main. Assim como na explicação da pilha, o código é executado de cima para baixo: primeiro a é empilhado, depois b. Os valores de a e b são salvos diretamente na pilha.


Por exemplo, os dados de tom estão no bloco 00001 do heap, a pilha guarda o endereço 00001 para localizar os dados posteriormente.
Quando a função Main concluir sua execução, a referência de endereço lucy: 01010 presente na pilha é desempilhada e destruída. Apenas o endereço é apagado, os dados reais do objeto no heap permanecem intactos. Nesse momento, o objeto do monte perde todas as referências registradas pelas variáveis da pilha.


Áreas de memória
Pilha e heap são divisões lógicas de memória em nível de software, não partições físicas do hardware.
Pilha e heap são apenas separações lógicas definidas pelo software; a memória RAM física não possui módulos de hardware dedicados marcados como “área de pilha” ou “área de heap”.
A RAM física é composta apenas por chips uniformes com endereços físicos sequenciais, não existe nenhum marcador de hardware que diferencie pilha e heap
O sistema operacional aloca um espaço de memória virtual independente para cada programa em execução e divide previamente essa memória virtual em quatro segmentos lógicos principais:
Segmento de Código: Armazena as instruções compiladas do programa
Segmento Estático Global: Variáveis estáticas, constantes
Segmento de Pilha (Stack): A thread principal e as threads secundárias do programa possuem cada uma um bloco de pilha independente
Segmento de Heap (Monte): Grande pool de memória compartilhado para alocações dinâmicas
A pilha e o heap aqui descritos são apenas áreas lógicas atribuídas pelo SO a cada processo, sem isolamento em nível de hardware entre ambos.
Pilha vs Heap