diff --git a/src/runtime/mgcscavenge.go b/src/runtime/mgcscavenge.go index db7c8121dc..b3f9cca10d 100644 --- a/src/runtime/mgcscavenge.go +++ b/src/runtime/mgcscavenge.go @@ -408,13 +408,20 @@ func (s *pageAlloc) scavengeOne(max uintptr, locked bool) uintptr { // Check the chunk containing the scav addr, starting at the addr // and see if there are any free and unscavenged pages. ci := chunkIndex(s.scavAddr) - base, npages := s.chunks[ci].findScavengeCandidate(chunkPageIndex(s.scavAddr), minPages, maxPages) + if s.summary[len(s.summary)-1][ci].max() >= uint(minPages) { + // We only bother looking for a candidate if there at least + // minPages free pages at all. It's important that we only + // continue if the summary says we can because that's how + // we can tell if parts of the address space are unused. + // See the comment on s.chunks in mpagealloc.go. + base, npages := s.chunks[ci].findScavengeCandidate(chunkPageIndex(s.scavAddr), minPages, maxPages) - // If we found something, scavenge it and return! - if npages != 0 { - s.scavengeRangeLocked(ci, base, npages) - unlockHeap() - return uintptr(npages) * pageSize + // If we found something, scavenge it and return! + if npages != 0 { + s.scavengeRangeLocked(ci, base, npages) + unlockHeap() + return uintptr(npages) * pageSize + } } unlockHeap() diff --git a/src/runtime/mgcscavenge_test.go b/src/runtime/mgcscavenge_test.go index b29a4d796a..518d5ab27a 100644 --- a/src/runtime/mgcscavenge_test.go +++ b/src/runtime/mgcscavenge_test.go @@ -373,6 +373,25 @@ func TestPageAllocScavenge(t *testing.T) { BaseChunkIdx + 1: {{0, PallocChunkPages}}, }, }, + "ScavDiscontiguous": { + beforeAlloc: map[ChunkIdx][]BitRange{ + BaseChunkIdx: {}, + BaseChunkIdx + 0xe: {}, + }, + beforeScav: map[ChunkIdx][]BitRange{ + BaseChunkIdx: {{uint(minPages), PallocChunkPages - uint(2*minPages)}}, + BaseChunkIdx + 0xe: {{uint(2 * minPages), PallocChunkPages - uint(2*minPages)}}, + }, + expect: []test{ + {2 * minPages * PageSize, 2 * minPages * PageSize}, + {^uintptr(0), 2 * minPages * PageSize}, + {^uintptr(0), 0}, + }, + afterScav: map[ChunkIdx][]BitRange{ + BaseChunkIdx: {{0, PallocChunkPages}}, + BaseChunkIdx + 0xe: {{0, PallocChunkPages}}, + }, + }, } for name, v := range tests { v := v