Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: sweeper can give up early and grow heap #21378

aclements opened this issue Aug 9, 2017 · 1 comment

runtime: sweeper can give up early and grow heap #21378

aclements opened this issue Aug 9, 2017 · 1 comment


Copy link

@aclements aclements commented Aug 9, 2017

mheap.reclaimList rotates swept spans to the back of whichever busy list it's sweeping and assumes that as soon as it encounters an already-swept span that it's done processing the whole list. However, gosweepone (used by proportional sweeping and the background sweeper) can sweep arbitrary spans, including spans that are in the middle of some busy list. If reclaimList encounters such a span, it won't sweep any more spans in that busy list. Eventually this can cause mheap.reclaim to fail even though there are sweepable spans, which leads to premature heap growth.

This is easy to demonstrate by adding the following to the end of reclaimList and running all.bash:

	for s := list.first; s != nil; s = {
		if s.sweepgen == sg-2 {
			println("unswept span", s)

This has been a problem for many releases, but AFAIK there aren't any specific reports of it. I found it by inspection.

/cc @RLH

@aclements aclements added this to the Go1.10 milestone Aug 9, 2017
@aclements aclements self-assigned this Aug 9, 2017
@rsc rsc modified the milestones: Go1.10, Go1.11 Nov 22, 2017
@aclements aclements modified the milestones: Go1.11, Unplanned Jul 3, 2018

This comment has been minimized.

Copy link

@gopherbot gopherbot commented Oct 2, 2018

Change mentions this issue: runtime: add an efficient whole-page reclaimer

@gopherbot gopherbot closed this in 6bd85f7 Nov 15, 2018
bradfitz pushed a commit that referenced this issue Nov 21, 2018
When we attempt to allocate an N page span (either for a large
allocation or when an mcentral runs dry), we first try to sweep spans
to release N pages. Currently, this can be extremely expensive:
sweeping a span to emptiness is the hardest thing to ask for and the
sweeper generally doesn't know where to even look for potentially
fruitful results. Since this is on the critical path of many
allocations, this is unfortunate.

This CL changes how we reclaim empty spans. Instead of trying lots of
spans and hoping for the best, it uses the newly introduced span marks
to efficiently find empty spans. The span marks (and in-use bits) are
in a dense bitmap, so these spans can be found with an efficient
sequential memory scan. This approach can scan for unmarked spans at
about 300 GB/ms and can free unmarked spans at about 32 MB/ms. We
could probably significantly improve the rate at which is can free
unmarked spans, but that's a separate issue.

Like the current reclaimer, this is still linear in the number of
spans that are swept, but the constant factor is now so vanishingly
small that it doesn't matter.

The benchmark in #18155 demonstrates both significant page reclaiming
delays, and object reclaiming delays. With "-retain-count=20000000
-preallocate=true -loop-count=3", the benchmark demonstrates several
page reclaiming delays on the order of 40ms. After this change, the
page reclaims are insignificant. The longest sweeps are still ~150ms,
but are object reclaiming delays. We'll address those in the next
several CLs.

Updates #18155.

Fixes #21378 by completely replacing the logic that had that bug.

Change-Id: Iad80eec11d7fc262d02c8f0761ac6998425c4064
Run-TryBot: Austin Clements <>
TryBot-Result: Gobot Gobot <>
Reviewed-by: Rick Hudson <>
@golang golang locked and limited conversation to collaborators Nov 15, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
None yet
3 participants
You can’t perform that action at this time.