mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: use inUse ranges to map in summary memory only as needed
Prior to this change, if the heap was very discontiguous (such as in TestArenaCollision) it's possible we could map a large amount of memory as R/W and commit it. We would use only the start and end to track what should be mapped, and we would extend that mapping as needed to accomodate a potentially fragmented address space. After this change, we only map exactly the part of the summary arrays that we need by using the inUse ranges from the previous change. This reduces the GCSys footprint of TestArenaCollision from 300 MiB to 18 MiB. Because summaries are no longer mapped contiguously, this means the scavenger can no longer iterate directly. This change also updates the scavenger to borrow ranges out of inUse and iterate over only the parts of the heap which are actually currently in use. This is both an optimization and necessary for correctness. Fixes #35514. Change-Id: I96bf0c73ed0d2d89a00202ece7b9d089a53bac90 Reviewed-on: https://go-review.googlesource.com/c/go/+/207758 Run-TryBot: Michael Knyszek <mknyszek@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
parent
6f2b8347b1
commit
1b1fbb3192
6 changed files with 200 additions and 133 deletions
|
|
@ -182,6 +182,10 @@ type pageAlloc struct {
|
|||
// runtime segmentation fault, we get a much friendlier out-of-bounds
|
||||
// error.
|
||||
//
|
||||
// To iterate over a summary level, use inUse to determine which ranges
|
||||
// are currently available. Otherwise one might try to access
|
||||
// memory which is only Reserved which may result in a hard fault.
|
||||
//
|
||||
// We may still get segmentation faults < len since some of that
|
||||
// memory may not be committed yet.
|
||||
summary [summaryLevels][]pallocSum
|
||||
|
|
@ -212,12 +216,9 @@ type pageAlloc struct {
|
|||
// making the impact on BSS too high (note the L1 is stored directly
|
||||
// in pageAlloc).
|
||||
//
|
||||
// summary[len(s.summary)-1][i] should always be checked, at least
|
||||
// for a zero max value, before accessing chunks[i]. It's possible the
|
||||
// bitmap at that index is mapped in and zeroed, indicating that it
|
||||
// contains free space, but in actuality it is unused since its
|
||||
// corresponding summary was never updated. Tests may ignore this
|
||||
// and assume the zero value (and that it is mapped).
|
||||
// To iterate over the bitmap, use inUse to determine which ranges
|
||||
// are currently available. Otherwise one might iterate over unused
|
||||
// ranges.
|
||||
//
|
||||
// TODO(mknyszek): Consider changing the definition of the bitmap
|
||||
// such that 1 means free and 0 means in-use so that summaries and
|
||||
|
|
@ -297,53 +298,6 @@ func (s *pageAlloc) init(mheapLock *mutex, sysStat *uint64) {
|
|||
s.mheapLock = mheapLock
|
||||
}
|
||||
|
||||
// extendMappedRegion ensures that all the memory in the range
|
||||
// [base+nbase, base+nlimit) is in the Ready state.
|
||||
// base must refer to the beginning of a memory region in the
|
||||
// Reserved state. extendMappedRegion assumes that the region
|
||||
// [base+mbase, base+mlimit) is already mapped.
|
||||
//
|
||||
// Note that extendMappedRegion only supports extending
|
||||
// mappings in one direction. Therefore,
|
||||
// nbase < mbase && nlimit > mlimit is an invalid input
|
||||
// and this function will throw.
|
||||
func extendMappedRegion(base unsafe.Pointer, mbase, mlimit, nbase, nlimit uintptr, sysStat *uint64) {
|
||||
if uintptr(base)%physPageSize != 0 {
|
||||
print("runtime: base = ", base, "\n")
|
||||
throw("extendMappedRegion: base not page-aligned")
|
||||
}
|
||||
// Round the offsets to a physical page.
|
||||
mbase = alignDown(mbase, physPageSize)
|
||||
nbase = alignDown(nbase, physPageSize)
|
||||
mlimit = alignUp(mlimit, physPageSize)
|
||||
nlimit = alignUp(nlimit, physPageSize)
|
||||
|
||||
// If none of the region is mapped, don't bother
|
||||
// trying to figure out which parts are.
|
||||
if mlimit-mbase != 0 {
|
||||
// Determine which part of the region actually needs
|
||||
// mapping.
|
||||
if nbase < mbase && nlimit > mlimit {
|
||||
// TODO(mknyszek): Consider supporting this case. It can't
|
||||
// ever happen currently in the page allocator, but may be
|
||||
// useful in the future. Also, it would make this function's
|
||||
// purpose simpler to explain.
|
||||
throw("mapped region extended in two directions")
|
||||
} else if nbase < mbase && nlimit <= mlimit {
|
||||
nlimit = mbase
|
||||
} else if nbase >= mbase && nlimit > mlimit {
|
||||
nbase = mlimit
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Transition from Reserved to Ready.
|
||||
rbase := add(base, nbase)
|
||||
sysMap(rbase, nlimit-nbase, sysStat)
|
||||
sysUsed(rbase, nlimit-nbase)
|
||||
}
|
||||
|
||||
// compareSearchAddrTo compares an address against s.searchAddr in a linearized
|
||||
// view of the address space on systems with discontinuous process address spaces.
|
||||
// This linearized view is the same one generated by chunkIndex and arenaIndex,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue