Go: Memory Management and Memory Sweep
By: Vincent Blanchon
Tags: Internals, Golang, Go, Garbage Collector

Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.

ℹ️ This article is based on Go 1.13. The notions about memory management discussed here are explained in my article “Go: Memory Management and Allocation.”

Sweeping the memory is a process that allows Go to know which memory segments are newly available for allocation. However, it does not clean the memory with resetting the bits to zero.

Zeroing the memory

The process of zeroing the memory — moving all bits to zero in the memory segment — is done on the fly during the allocation:

Zeroing the memory

However, we could wonder what strategy Go uses to know which objects are available for allocation. Go actually tracks the free objects thanks to an internal bitmap in each span called allocBits . Let’s review its workflow, starting with the initial state:

Free objects tracking with allocBits

Performance-wise, allocBits will represent the initial state and will remain the same, but it is helped by freeIndex , an incremental counter that points to the first free slot.

Then, the first allocation starts:

Free objects tracking with allocBits

freeIndex is now incremented and knows the next free slot based on allocBits .

Allocations will occur again and, later, the garbage collector will run to free the unused memory. During the marking phase, the garbage collector tracks the memory in-use with a bitmap named gcmarkBits . Let’s take the same example where the first block is not used anymore by our running program:

Memory tracking during the garbage collector

Memory in-use is colored in black while the memory not reachable from the current execution will stay in white.

For more information about the marking and coloring phase, I suggest you read my article “Go: How Does the Garbage Collector Mark the Memory?.”

We now have an accurate view of the memory available for allocation with gcmarkBits . Go can now replace allocBits with gcmarkBits , this operation is the sweep of the memory:

Sweeping a span

However, this must be done on each span and can take a lot of time. Go aims not to block the execution while sweeping the memory and provides two strategies for that.

Sweeping phase

Go provides two ways to sweep the memory:

with a worker waiting in background that sweeps the spans one by one

on the fly when the allocation requires a span

Regarding the worker in background, when starting to run your program, Go will set up a background worker — with the only task to sweep the memory — that will sleep and wait for spans to sweep:

Background sweeper

We also can see this background worker always coming for sweeping through the cycles in the traces:

Background sweeper

The second way to sweep the spans is to do it on the fly. However, since the spans have already been dispatched to the local cache mcache of each processor, it is hard to track which ones need to be swept first. This is why Go moves all the spans to the mcentral first:

Spans are released to the central list

Then, it will let the local cache mcache to request them again with sweeping on the fly:

Sweep span on the fly during allocation

Sweeping on the fly ensures all spans will get swept while saving resources and not blocking the execution.

Collision with Collection Cycles

As seen in the previous section, sweeping can take some time since the only worker is sweeping the spans in the background. However, we could wonder what would happen if another garbage collection cycle starts when the sweeping is not done. In this case, the goroutine that runs the garbage collector will help to finish the remaining sweep before starting the marking phase. Let’s take an example with an allocation of thousands of objects with two successive calls to the garbage collector:

Sweeping must be finished before a new cycle

However, this case should not happen if the garbage collector is not forced by the developer. The sweeping that runs in background along with the one done on the fly should be enough since the amount of spans to sweep is proportional to the allocations required to trigger a new cycle.