Featured image of post Garbage Collection in Golang

Garbage Collection in Golang

This article explains how Go’s garbage collection works, its advantages, and how to optimize your code to work efficiently with it. Imagine you have a toy box (this is your computer’s memory).

How Memory Gets Used

When you create a variable in Go, like:

name := "Gopher"

Here’s what happens when you create a variable in Go:

  1. Go asks the toy box (memory heap): “I need some space to put this name”

  2. The toy box gives Go a special spot (memory address)

  3. Go puts “Gopher” in that spot (writes data to memory)

  4. Go tells your program: “I put ‘Gopher’ here, remember this spot!” (creates a reference or pointer)

When you create bigger things like lists or objects, Go asks for bigger spaces in the toy box (memory heap).

How the Garbage Collector Works

Now imagine you’re playing with toys:

  1. Finding Toys You’re Using

    • The garbage collector is like a friendly helper who checks which toys you’re still playing with

    • It looks at what toys you’re holding in your hands (variables in scope)

    • It also checks what toys those toys might be connected to (references and pointers)

  2. Cleaning Up

    • After finding all the toys you’re still using, it looks at the rest

    • Any toy that you’re not holding onto anymore gets put away (unreferenced memory)

    • The space in the toy box is now free to use for new toys (memory is freed)

For example, if you do this:

name := "Gopher"
name = "Super Gopher"

The original “Gopher” is now like a toy you dropped (it becomes unreachable). You’re now holding “Super Gopher” instead. The garbage collector notices you dropped “Gopher” and cleans it up (deallocates the memory).

What Makes Go’s Garbage Collector Special

Go’s garbage collector is really good because:

  1. It cleans up while you play - You don’t have to stop playing with your toys while it cleans up (concurrent collection)

  2. It works quickly - It doesn’t make you wait very long (low-latency pauses)

  3. It’s automatic - You don’t have to remember to clean up yourself (automatic memory management)

Now here is how it technically works with all the technical jargon : |

Go (or Golang) implements a concurrent mark-and-sweep garbage collector that has seen significant improvements over the years.

Go uses a non-generational concurrent mark-and-sweep garbage collector. Unlike other languages that require manual memory management (like C) or have complex generational collectors (like Java).

Now you must be wondering what is Generational and Non-Generational, to explain it better let understand it like this, Imagine you’re cleaning up your toys (memory in a computer). Some toys are used a lot (like your favorite LEGO), and some are barely touched (like that puzzle you did once and forgot).

Most Cleaners (Other Languages)

They sort toys into two boxes:

  • New Toys (used recently) → Check often (clean quickly).
  • Old Toys (not used much) → Check rarely (clean slowly).

(This is called generational GC—it assumes new stuff is garbage sooner.)

Go’s Cleaner

Go says: “Sorting is extra work! Let’s just check ALL toys at once, but SUPER FAST!” 🚀

  • It doesn’t separate new/old toys (no generational boxes).

  • Instead, it quietly cleans while you play (concurrent GC).

  • Pauses are so short (like a blink) you barely notice!

To achieve that golang’s GC goes through following phases:

  1. Mark Phase: The GC identifies all reachable objects in memory by starting from known roots (global variables, stack variables) and walking through pointers.

  2. Sweep Phase: Any memory that wasn’t marked as reachable is considered garbage and is freed.

Go’s collector operates concurrently with the application code, minimizing pause times. Since Go 1.5, the garbage collector has been fully concurrent, and with Go 1.8+, GC pauses are typically under 1ms.

Key Features of Go’s Garbage Collector

Non-generational Design

Go’s GC doesn’t separate objects into generations based on age, unlike Java or .NET. This simplifies the design but can sometimes be less efficient for certain workloads, for further reading why.

Concurrent Collection

The collector runs concurrently with your application, meaning your program doesn’t need to stop completely while garbage collection occurs. This helps maintain consistent performance.

Low Latency Focus

Go’s garbage collector is optimized for latency, not throughput. It may do more frequent collections to keep pause times minimal, even at the expense of some CPU overhead.

Tri-color Algorithm

Go uses a tri-color mark-and-sweep algorithm:

  • White: Objects not yet examined

  • Grey: Objects being examined

  • Black: Objects that have been examined and are reachable

Things to remember while writing code to optimize memory usage

Preallocate Memory

When you know how much memory you need, preallocate it:

// Instead of this:
s := make([]int, 0)
for i := 0; i < 10000; i++ {
    s = append(s, i)
}
// Do this:
s := make([]int, 0, 10000)
for i := 0; i < 10000; i++ {
    s = append(s, i)
}

Avoid Unnecessary Allocations

Small changes can make a big difference:

// This creates a new string on each iteration
for i := 0; i < 1000; i++ {
    s := fmt.Sprintf("prefix-%d", i)
    // use s
}
// Better approach with a buffer
var buffer strings.Builder
for i := 0; i < 1000; i++ {
    buffer.Reset()
    fmt.Fprintf(&buffer, "prefix-%d", i)
    s := buffer.String()
    // use s
}

Monitor GC Behavior

Go provides tools to monitor garbage collection:

  • Set GODEBUG=gctrace=1 environment variable to see GC logs

  • Use runtime/debug.ReadGCStats() for programmatic access

  • Leverage pprof for more detailed profiling

Go’s garbage collector offers a good balance between ease of use and performance. While it doesn’t require much tuning for most applications, understanding how it works can help you write more efficient code, especially for high-performance systems.

Privacy Policy