Advanced Go (Golang) Notes
System Internals, Concurrency & Backend Architecture
1. The Go Scheduler (G-M-P Model)
Go uses an M:N scheduling model, multiplexing M goroutines onto N OS threads. The runtime scheduler
operates on a Work-Stealing paradigm, ensuring high core utilization and minimal context-switch
overhead compared to OS-level threads.
The G, M, and P
• G (Goroutine): Represents a goroutine. Contains the stack, instruction pointer, and scheduling state.
• M (Machine): Represents an OS thread. M executes the Gs. M must hold a P to execute Go code.
• P (Processor): Represents a logical processor (configured via GOMAXPROCS ). P holds a local queue
of runnable Gs.
Scheduling Mechanics
When an M is idle, it looks for a runnable G from its P's local queue. If empty, it attempts to steal half the
Gs from another P's queue. If a G makes a blocking syscall, the M is detached from the P, leaving the P
free to acquire another M to continue executing other Gs.
// Controlling logical processors
import "runtime"
func init() {
// Defaults to [Link]() in modern Go
[Link]([Link]() * 2)
}
2. Memory Management & Escape Analysis
Go leverages a combination of stack allocation and heap allocation. Escape analysis is the compiler
phase that determines whether a variable can safely reside on the stack or must be allocated on the
heap.
Escape Analysis Rules
Data escapes to the heap if:
• It is returned as a pointer from a function.
• It is assigned to a global variable.
• Its size is dynamic or too large for the stack.
• It is stored in an interface (e.g., passed to [Link] ).
Diagnostic Tooling: Run go build -gcflags="-m" to view escape analysis decisions.
Minimizing heap allocations reduces garbage collection pressure.
Garbage Collection
Go uses a concurrent, non-generational, tri-color mark-and-sweep garbage collector. The focus is on
ultra-low latency (sub-millisecond pauses) rather than maximum throughput.
• Mark phase: Objects are colored white (unreachable), grey (reachable, children unvisited), or black
(reachable, children visited). Uses write barriers to ensure consistency while the application runs
concurrently.
• Sweep phase: Reclaims memory of white objects. Usually runs concurrently in the background.
• GOGC: The single tunable parameter. GOGC=100 means GC triggers when the heap grows by
100% since the last collection.
3. Concurrency Synchronization
While "share memory by communicating" (channels) is the Go idiom, the sync package provides
crucial low-level primitives for high-performance data structures.
Channels vs. Mutexes
Use channels for orchestrating control flow and passing ownership of data. Use [Link] or
[Link] for protecting internal state of a struct. RWMutex optimizes read-heavy workloads but
carries higher lock-acquisition overhead.
type SafeCache struct {
mu [Link]
m map[string]interface{}
}
func (c *SafeCache) Get(key string) (interface{}, bool) {
[Link]()
defer [Link]()
val, ok := c.m[key]
return val, ok
}
Context
The context package is mandatory for propagating cancellation signals, timeouts, and request-scoped
values across API boundaries and goroutines. Never store contexts in structs; pass them explicitly as the
first argument.
func FetchData(ctx [Link], url string) error {
req, _ := [Link](ctx, "GET", url, nil)
resp, err := [Link](req)
// ...
}
4. Interfaces and Type System
Go uses structural typing (duck typing). Interfaces are satisfied implicitly. Interfaces are implemented as
a two-word structure: a pointer to the type information (itable) and a pointer to the data.
Generics (Type Parameters)
Introduced in Go 1.18, generics allow writing functions and data structures that operate on any type,
evaluated at compile time via monomorphization.
// Generic constraint
type Number interface {
int | int64 | float64
}
func Sum[T Number](slice []T) T {
var total T
for _, v := range slice {
total += v
}
return total
}
5. High-Performance Backend Patterns
Building scalable backend systems requires leveraging Go's standard library and profiling tools
efficiently.
Connection Pooling
Database connections and HTTP clients maintain internal connection pools. Always reuse [Link] and
[Link] objects. Ensure HTTP response bodies are closed and fully read to return the connection
to the pool.
resp, err := [Link]("[Link]
if err != nil { return err }
defer [Link]()
[Link]([Link], [Link]) // Crucial for connection reuse
Profiling (pprof)
Import _ "net/http/pprof" to expose runtime profiling data over HTTP. Use go tool pprof to
analyze CPU bottlenecks, heap allocations, and goroutine blocking profiles in production.