Sobes.tech
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:

  1. Disparador de ativação: O recolhedor inicia-se quando o volume de memória alocada pelo programa excede um limiar, que é regulado dinamicamente.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
}