Sobes.tech
Middle+

Comment fonctionne le ramasse-miettes dans le langage Go?

sobes.tech IA

Réponse de l'IA

Le ramasse-miettes en Go implémente une méthode parallèle, non précise (non générationnelle), basée sur l'algorithme Mark-and-Sweep avec un déclencheur basé sur le volume du tas.

Principes de fonctionnement principaux :

  1. Déclencheur d'activation : Le ramasse-miettes démarre lorsque le volume de mémoire allouée par le programme dépasse un seuil, qui est régulé dynamiquement.
  2. Phase de marquage (Mark Phase) :
    • Le ramasse-miettes suspend uniquement la partie critique de la phase de marquage (Stop-the-World, STW), mais cela dure très peu.
    • En parallèle avec le programme en cours (mutator), le ramasse-miettes parcourt le graphe d'objets à partir des pointeurs racine (registres, variables globales, piles de goroutines).
    • Les objets atteignables (vivants) sont marqués comme utilisés.
  3. Phase de balayage (Sweep Phase) :
    • Après avoir terminé la phase de marquage, le ramasse-miettes parcourt la liste des spans de mémoire.
    • Les objets non marqués sont considérés comme des déchets et leur mémoire est libérée.
    • Cette phase est également effectuée en parallèle avec l'exécution du programme.
  4. Barrière d'écriture (Write Barrier) : Go utilise une barrière d'écriture pour suivre les modifications dans le graphe d'objets pendant la phase de marquage parallèle. Cela garantit le bon fonctionnement du ramasse-miettes, malgré les modifications de mémoire par le mutator.
  5. Pas de générations : Contrairement à certains autres langages, Go ne divise pas les objets en générations. Le ramasse-miettes traite toute la heap en une seule fois.
  6. Objectif de faible latence : Lors de la conception du GC de Go, l'objectif était de minimiser les pauses dues à la collecte de déchets (STW), ce qui le rend adapté aux applications serveur.

La parallélisation et l'utilisation de la barrière d'écriture permettent au ramasse-miettes de minimiser le temps d'arrêt du programme, assurant de hautes performances.

Exemple de gestion de mémoire simplifiée (pas directement GC, mais illustrant la libération de ressources) :

package main

import (
	"fmt"
	"os"
)

func main() {
	// Exemple de création d'une ressource temporaire qui sera libérée
	file, err := os.Create("temp.txt")
	if err != nil {
		fmt.Println("Erreur lors de la création du fichier :", err)
		return
	}
	// Fermeture différée du fichier.
	// Bien que ce ne soit pas du GC à proprement parler, cela montre la libération de ressources.
	defer file.Close()

	fmt.Println("Fichier 'temp.txt' créé.")

	// Ensuite, le programme peut travailler avec le fichier...

	// Le GC fonctionne automatiquement en arrière-plan pour collecter les objets inutilisés dans la heap.
	// Par exemple, si l'on crée une tranche ou une carte :
	data := make([]int, 1000000) // Allocation dans la heap
	_ = data // Utilisation

	// Lorsque data n'est plus accessible (par exemple, en sortant de la fonction),
	// le ramasse-miettes libérera avec le temps la mémoire allouée.
}