Middle+
Como funciona o coletor de lixo na linguagem Go?
sobes.tech IA
Resposta da IA
O recolhedor de lixo em Go implementa um método paralelo, impreciso (não geracional), baseado no algoritmo Mark-and-Sweep com um disparador por volume de heap.
Princípios básicos de funcionamento:
- Disparador de ativação: O recolhedor inicia-se quando o volume de memória alocada pelo programa excede um limiar, que é regulado dinamicamente.
- Fase de marca (Mark Phase):
- O recolhedor pausa apenas a parte crítica da fase de marca (Stop-the-World, STW), mas isso dura muito pouco.
- Em paralelo com o programa em execução (mutator), o recolhedor percorre o grafo de objetos a partir dos ponteiros raiz (registos, variáveis globais, pilhas de goroutines).
- Os objetos alcançáveis (vivos) são marcados como utilizados.
- Fase de varredura (Sweep Phase):
- Após completar a fase de marca, o recolhedor percorre a lista de spans de memória.
- Os objetos não marcados são considerados lixo e a sua memória é libertada.
- Esta fase também é executada em paralelo com a execução do programa.
- Barreira de escrita (Write Barrier): Go usa uma barreira de escrita para rastrear alterações no grafo de objetos durante a fase de marca em paralelo. Isto garante o funcionamento correto do recolhedor, apesar das modificações na memória pelo mutator.
- Sem gerações: Ao contrário de algumas outras linguagens, Go não divide os objetos em gerações. O recolhedor processa toda a heap de uma só vez.
- Objetivo de baixa latência: Ao projetar o GC de Go, foi definido o objetivo de minimizar as pausas causadas pela recolha de lixo (STW), tornando-o adequado para aplicações servidoras.
A paralelização e o uso da barreira de escrita permitem ao recolhedor minimizar o tempo de paragem do programa, garantindo alto desempenho.
Exemplo de gestão de memória simplificada (não diretamente GC, mas ilustra a libertação de recursos):
package main
import (
"fmt"
"os"
)
func main() {
// Exemplo de criação de um recurso temporário que será libertado
file, err := os.Create("temp.txt")
if err != nil {
fmt.Println("Erro ao criar o ficheiro:", err)
return
}
// Fecho diferido do ficheiro.
// Embora isto não seja GC em sentido estrito, demonstra a libertação de recursos.
defer file.Close()
fmt.Println("Ficheiro 'temp.txt' criado.")
// Depois, o programa pode trabalhar com o ficheiro...
// O GC funciona automaticamente em segundo plano para recolher objetos não utilizados na heap.
// Por exemplo, se criar um slice ou um mapa:
data := make([]int, 1000000) // Alocação na heap
_ = data // Uso
// Quando data deixar de ser acessível (por exemplo, ao sair da função),
// o recolhedor de lixo libertará com o tempo a memória alocada.
}