Heap e Stack e suas diferenças em golang
Heap
Região de memória usada para alocação dinâmica de dados com tempo de vida variável.
É mais flexível e, em geral, tem custo maior de alocação/acesso que a stack.
Em Go, objetos no heap são gerenciados pelo Garbage Collector (GC).
Stack
Armazena os frames de chamadas de função (parâmetros, variáveis locais, endereço de retorno etc.).
Segue o modelo LIFO (Last In, First Out): a última chamada a entrar é a primeira a sair.
Quando a função termina, seu frame é removido automaticamente (sem GC).
Stack no Go
A stack no Go funciona de forma dinâmica através de cópia de stack (stack copying), trazendo alta eficiência de memória e bom desempenho.
- Cada goroutine inicia com apenas 2 KB de stack.
- Conforme a goroutine vai sendo executada, o runtime verifica se há necessidade de mais espaço na stack
- Conforme a execução avança e mais espaço é necessário, o runtime aloca uma nova stack maior (geralmente o dobro do tamanho anterior).
- Todo o conteúdo da stack antiga é copiado para a nova, e todos os ponteiros são atualizados.
- Quando o uso diminui, a stack pode ser reduzida novamente.
Guard Page e Stack Overflow
Stack Overflow: O runtime do Go faz verificações proativas para impedir que a stack cresça indefinidamente, gerando um erro controlado (runtime error: stack overflow) quando o limite é atingido.
Guard Page: Região de proteção no final da stack que ajuda a detectar a necessidade de crescimento e previne acessos inválidos de memória (segmentation faults).
Heap e Stack e suas diferenças
Stack (Pilha)
A stack é uma região de memória usada para armazenar:
- chamadas de funções;
- parâmetros;
- variáveis locais;
- endereços de retorno;
- informações de controle da execução.
Ela segue a estrutura LIFO (Last In, First Out): o último item que entra é o primeiro que sai.
Quando uma função é chamada, um novo frame é empilhado na stack. Quando a função termina, esse frame é removido automaticamente.
Exemplo:
func soma(a int, b int) int {
resultado := a + b
return resultado
}Durante a execução:
a,beresultadoficam na stack;- ao finalizar a função, essa memória é liberada automaticamente.
Características da Stack
- Muito rápida;
- Gerenciada automaticamente;
- Possui tamanho mais limitado;
- Ideal para dados pequenos e temporários;
- Não precisa do Garbage Collector.
Heap
O heap é uma região de memória usada para alocação dinâmica de objetos e estruturas maiores ou que precisam sobreviver após o término de uma função.
Diferente da stack, o heap:
- não segue LIFO;
- cresce e diminui dinamicamente;
- possui gerenciamento mais complexo;
- normalmente depende do Garbage Collector.
Exemplo em Go:
type User struct {
Name string
}
func createUser() *User {
user := User{Name: "Rafael"}
return &user
}Nesse caso:
userpode escapar da stack;- o compilador coloca o objeto no heap;
- o Garbage Collector libera a memória quando não houver mais referências.
Características do Heap
- Mais flexível;
- Maior capacidade de memória;
- Mais lento que a stack;
- Usado para objetos compartilhados ou persistentes;
- Precisa de gerenciamento de memória (GC no Go).
Diferença entre Stack e Heap
| Stack | Heap |
|---|---|
| Memória rápida | Memória mais lenta |
| Estrutura LIFO | Sem ordem específica |
| Gerenciamento automático | Gerenciado pelo GC |
| Variáveis locais e chamadas de função | Objetos dinâmicos |
| Menor capacidade | Maior capacidade |
| Liberação imediata ao fim da função | Liberação depende do GC |
Observação importante em Go
Em Go, não é o programador que escolhe diretamente entre stack e heap. O compilador faz isso usando uma análise chamada:
- escape analysis.
Se o valor não precisa sobreviver fora da função, normalmente fica na stack.
Se precisar sobreviver ou ser compartilhado, vai para o heap.
Exemplo:
go build -gcflags="-m"Esse comando mostra quais variáveis “escaparam” para o heap.
Ultima dica
package main
import "fmt"
func Total[T float64 | int](a map[string]T) T {
var sum T
for _, v := range a {
sum += v
}
return sum
}
func main() {
a := map[string]float64{
"apple": 1.5,
"banana": 2.0,
"orange": 3.0,
}
b := map[string]int{
"apple": 1,
"banana": 2,
"orange": 3,
}
fmt.Println(Total(a)) // Output: 6.5
println(Total(b)) // Output: 6
}O fmt.Println faz com que o valor passado (no caso, o resultado de Total(a)) escape para a heap, enquanto o println (builtin) geralmente não faz.
Por que isso acontece?
- Ele usa interface{}
- Usa abstrações pesadas (reflection)

0 comentários