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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

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

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.