DevPrep
  • Interview Prep
  • Projects
  • Resources
  • Pricing
  • About Us
Submit Question
DevPrep
  • Pricing
  • About Us
Submit Question

Practice

  • JavaScript
  • DSA
  • Machine Coding
  • System Design

Resources

  • Learning Tracks
  • Articles
  • Roadmaps
  • Compare Concepts
  • Glossary
  • Developer Tools
  • All Questions

Company

  • About
  • Pricing

Legal

  • Privacy Policy
  • Terms of Service
DevPrep

© 2026 DevPrep. All rights reserved.

← Back to Questions
HardTheory

Go Garbage Collection: Tri-Color Mark and Sweep

17 views

Problem Statement

Your high-frequency trading system written in Go has latency spikes every few seconds. Using GODEBUG, you discover GC pauses are the culprit. Explain Go's garbage collector and how to tune it for low-latency applications.

Tri-Color Mark and Sweep Algorithm

Go uses a concurrent, tri-color, mark-and-sweep garbage collector:

  • White: Not yet scanned (candidates for collection)
  • Grey: Scanned, but children not scanned
  • Black: Scanned, and all children scanned (reachable)

GC Phases


┌────────────────────────────────────────────────────────┐
│ 1. Mark Setup (STW ~10-30μs)                          │
│    - Enable write barrier                              │
│    - Scan stacks                                       │
├────────────────────────────────────────────────────────┤
│ 2. Concurrent Mark (runs with app)                    │
│    - Traverse heap                                     │
│    - Mark reachable objects                            │
├────────────────────────────────────────────────────────┤
│ 3. Mark Termination (STW ~10-30μs)                    │
│    - Drain remaining work                              │
│    - Disable write barrier                             │
├────────────────────────────────────────────────────────┤
│ 4. Sweep (concurrent)                                 │
│    - Reclaim white objects                             │
│    - Return memory to allocator                        │
└────────────────────────────────────────────────────────┘

Debugging GC with GODEBUG

GODEBUG=gctrace=1 ./myapp

# Output:
# gc 1 @0.012s 2%: 0.018+1.2+0.019 ms clock, 0.14+0.20/0.80/0+0.15 ms cpu, 
#              4->4->1 MB, 5 MB goal, 8 P
#
# Meaning:
# gc 1           - GC number
# @0.012s        - Time since start
# 2%             - % of CPU used for GC
# 0.018+1.2+0.019 - STW mark setup, concurrent mark, STW mark term
# 4->4->1 MB     - Heap before GC, after GC, live data

Tuning GC for Low Latency

import "runtime/debug"

func init() {
    // GOGC: Target heap growth % before next GC
    // Default: 100 (double heap size triggers GC)
    // Lower = more frequent GC, less memory, more CPU
    debug.SetGCPercent(50)
    
    // GOMEMLIMIT: Soft memory limit (Go 1.19+)
    // GC triggers more aggressively as limit approaches
    debug.SetMemoryLimit(1 << 30) // 1GB
}

Reducing GC Pressure

// 1. Use sync.Pool for frequently allocated objects
var bufferPool = sync.Pool{
    New: func() interface{} {
        return make([]byte, 4096)
    },
}

func process(data []byte) {
    buf := bufferPool.Get().([]byte)
    defer bufferPool.Put(buf)
    // Use buf...
}

// 2. Pre-allocate slices
// Bad: grows and reallocates
items := []Item{}
for i := 0; i < 1000; i++ {
    items = append(items, Item{})
}

// Good: single allocation
items := make([]Item, 0, 1000)
for i := 0; i < 1000; i++ {
    items = append(items, Item{})
}

// 3. Use value types instead of pointers when possible
// (keeps data on stack, not heap)

The Ballast Technique (Legacy)

// Before GOMEMLIMIT: Allocate a large unused byte slice
// to trick GC into running less frequently
var ballast = make([]byte, 10<<30) // 10GB

// This is now obsolete with GOMEMLIMIT in Go 1.19+

Follow-up Questions

  1. Is Go's GC generational? Compacting?
  2. What is a write barrier and why is it needed?
  3. How does sync.Pool interact with GC cycles?

Sample Test Cases

Case 1
Input
objects: root->A->B, B unreachable
Expected Output
B marked white, collected
Case 2
Input
concurrent mutation during GC
Expected Output
Write barrier maintains invariant

No solutions yet

Be the first to share a solution for this question.

Comments (0)

Sign in to leave a comment.

No comments yet. Be the first to comment.

Stats

Views
17
Likes
0
Solutions
0
Comments
0

Category

Backend Engineering