How Go’s GC Skips Scanning Pointer‑Free Objects – Deep Dive into the noscan Optimization
This article explains why and how the Go runtime’s garbage collector can skip scanning objects that contain no pointers, describes the noscan flag and its implementation in the GC pipeline, shows benchmark results, and offers practical tips for leveraging this optimization in real‑world code.
Today we explore a common optimization in the official Go compiler and runtime (gc compiler) during the mark‑scan phase of garbage collection.
What does "skip scanning" mean?
When the GC checks object liveness, it must also examine the object's sub‑objects (elements of arrays/slices, map keys and values, struct fields) because they may reference other objects. Skipping scanning means the GC only checks the object itself and does not recurse into its children.
Which objects can be skipped?
The key criterion is the noscan flag on the memory span (mspan). If the flag is set, the GC will not scan the span’s contents. This applies to maps, slices, structs, or any allocation whose type contains no pointers.
How the skip works in the GC pipeline
The GC starts by marking root objects, then processes work items from a queue ( gcWork). Functions involved include: gcDrain – pulls objects from the queue and calls scanobject. scanobject – scans an object's sub‑objects unless the span is marked noscan. greyobject – marks an object and checks span.spanclass.noscan(); if true, it returns early, preventing further scanning.
The essential check is:
if span.spanclass.noscan() {
return
}Thus, the presence of the noscan flag completely controls whether an object’s children are scanned.
How the noscan flag is set
During memory allocation the runtime creates a spanClass via makeSpanClass(sizeclass, noscan). The noscan value is derived from the type information: noscan := typ == nil || !typ.Pointers() In mallocgc (runtime/malloc.go) the flag is passed to makeSpanClass for both small and large allocations. For slices, makeslice forwards the element type to mallocgc; if the element type has no pointers, the resulting slice’s backing memory is marked noscan. For maps, the bucket array is allocated similarly; when the map’s key and value types contain no pointers, the bucket span receives the noscan flag.
Performance impact
A benchmark creates many large slices of int64 (pointer‑free) versus slices of *int64 (pointer‑containing). The GC time for the pointer‑free case is about 21 % faster while memory allocations remain identical:
GCScan-8 379.0µs ± 2% 298.0µs ± 2% -21.51%This demonstrates that skipping the scan of pointer‑free objects can significantly reduce GC overhead, especially for large data structures.
Practical usage
When designing caches or other in‑memory structures, avoid storing pointers inside maps or slices whenever possible. For example, instead of a map of pointers, store values in a slice and keep a map from keys to slice indices. This eliminates pointers from the map, allowing the GC to skip scanning the map’s entries:
type Cache[K comparable, V any] struct {
buf []V
m map[K]int // index into buf
}
func (c *Cache[K, V]) Get(key K) *V {
if idx, ok := c.m[key]; ok {
return &c.buf[idx]
}
return nil
}
func (c *Cache[K, V]) Set(key K, value V) {
if idx, ok := c.m[key]; ok {
c.buf[idx] = value
return
}
c.m[key] = len(c.buf)
c.buf = append(c.buf, value)
}Such designs trade a bit of extra memory and complexity for reduced GC pressure.
Conclusion
The Go runtime skips scanning objects whose memory spans are marked noscan, which is determined at allocation time based on whether the type contains pointers. This optimization applies to maps, slices, structs, and any pointer‑free allocation, yielding noticeable GC performance gains. While the optimization is specific to the official Go compiler, minimizing pointer usage remains a universal technique for improving GC efficiency across languages.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
