Operations 20 min read

Why Go’s GC Skips Scanning Pointer‑Free Objects and How It Boosts Performance

This article explains the Go runtime’s garbage‑collector optimization that skips scanning objects without pointers, describes how the noscan flag is set during memory allocation, shows the code paths that enforce the skip, benchmarks the performance gain, and offers practical tips for applying the technique in real‑world Go programs.

Raymond Ops
Raymond Ops
Raymond Ops
Why Go’s GC Skips Scanning Pointer‑Free Objects and How It Boosts Performance

What Skipping Scanning Means

During the mark phase the GC checks an object’s sub‑objects (elements of arrays/slices, map keys and values, struct fields). Skipping scanning means the GC only checks the object itself and does not descend into its children.

Which Objects Can Be Skipped

If a map’s keys and values contain no pointers, the GC can treat the whole map as a single root and avoid scanning its internal storage. The same applies to slices, arrays, and structs whose fields contain no pointers.

How Skipping Is Implemented

The GC starts with root objects, processes them via

scanblock

or

markrootSpans

, and then calls

greyobject

.

greyobject

checks

s.spanclass.noscan()

; if the span is marked noscan the function returns without enqueuing the object for further scanning.

How the noscan Flag Is Set

When memory is allocated (e.g., via

mallocgc

), the runtime determines whether the type contains pointers. If the type has no pointers,

noscan

is set by creating a

spanClass

with

makeSpanClass(sizeclass, true)

. This flag propagates to slices (via

makeslice

), maps (via

makeBucketArray

), and other allocations.

Performance Impact

Benchmarks comparing a slice of

int64

(noscan) with a slice of

*int64

(scanned) show a ~21% reduction in GC pause time while memory usage remains the same, demonstrating that skipping scanning can significantly speed up GC for pointer‑free data.

Practical Usage

To benefit from this optimization, prefer pointer‑free types for large collections. For caches that need mutable values, store values in a slice and map keys to slice indices, eliminating pointers in the map itself. This reduces GC work at the cost of extra memory and slightly more complex insertion/deletion logic.

Conclusion

The Go compiler marks memory allocated for pointer‑free types with the noscan flag, allowing the GC to skip deep scans of those objects. While this behavior is specific to the official Go compiler, minimizing pointer usage is a universal technique for reducing GC overhead.

Performance OptimizationMemory ManagementGoGarbage Collectionnoscan
Raymond Ops
Written by

Raymond Ops

Linux ops automation, cloud-native, Kubernetes, SRE, DevOps, Python, Golang and related tech discussions.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.