2022-03-15 00:23:26 +00:00
|
|
|
// Copyright 2022 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package runtime
|
|
|
|
|
|
|
|
|
|
import "unsafe"
|
|
|
|
|
|
|
|
|
|
// OS memory management abstraction layer
|
|
|
|
|
//
|
|
|
|
|
// Regions of the address space managed by the runtime may be in one of four
|
|
|
|
|
// states at any given time:
|
|
|
|
|
// 1) None - Unreserved and unmapped, the default state of any region.
|
|
|
|
|
// 2) Reserved - Owned by the runtime, but accessing it would cause a fault.
|
|
|
|
|
// Does not count against the process' memory footprint.
|
|
|
|
|
// 3) Prepared - Reserved, intended not to be backed by physical memory (though
|
|
|
|
|
// an OS may implement this lazily). Can transition efficiently to
|
|
|
|
|
// Ready. Accessing memory in such a region is undefined (may
|
|
|
|
|
// fault, may give back unexpected zeroes, etc.).
|
|
|
|
|
// 4) Ready - may be accessed safely.
|
|
|
|
|
//
|
|
|
|
|
// This set of states is more than is strictly necessary to support all the
|
|
|
|
|
// currently supported platforms. One could get by with just None, Reserved, and
|
|
|
|
|
// Ready. However, the Prepared state gives us flexibility for performance
|
|
|
|
|
// purposes. For example, on POSIX-y operating systems, Reserved is usually a
|
|
|
|
|
// private anonymous mmap'd region with PROT_NONE set, and to transition
|
|
|
|
|
// to Ready would require setting PROT_READ|PROT_WRITE. However the
|
|
|
|
|
// underspecification of Prepared lets us use just MADV_FREE to transition from
|
|
|
|
|
// Ready to Prepared. Thus with the Prepared state we can set the permission
|
|
|
|
|
// bits just once early on, we can efficiently tell the OS that it's free to
|
|
|
|
|
// take pages away from us when we don't strictly need them.
|
|
|
|
|
//
|
|
|
|
|
// This file defines a cross-OS interface for a common set of helpers
|
|
|
|
|
// that transition memory regions between these states. The helpers call into
|
|
|
|
|
// OS-specific implementations that handle errors, while the interface boundary
|
|
|
|
|
// implements cross-OS functionality, like updating runtime accounting.
|
|
|
|
|
|
|
|
|
|
// sysAlloc transitions an OS-chosen region of memory from None to Ready.
|
|
|
|
|
// More specifically, it obtains a large chunk of zeroed memory from the
|
|
|
|
|
// operating system, typically on the order of a hundred kilobytes
|
|
|
|
|
// or a megabyte. This memory is always immediately available for use.
|
|
|
|
|
//
|
runtime: track how much memory is mapped in the Ready state
This change adds a field to memstats called mappedReady that tracks how
much memory is in the Ready state at any given time. In essence, it's
the total memory usage by the Go runtime (with one exception which is
documented). Essentially, all memory mapped read/write that has either
been paged in or will soon.
To make tracking this not involve the many different stats that track
mapped memory, we track this statistic at a very low level. The downside
of tracking this statistic at such a low level is that it managed to
catch lots of situations where the runtime wasn't fully accounting for
memory. This change rectifies these situations by always accounting for
memory that's mapped in some way (i.e. always passing a sysMemStat to a
mem.go function), with *two* exceptions.
Rectifying these situations means also having the memory mapped during
testing being accounted for, so that tests (i.e. ReadMemStats) that
ultimately check mappedReady continue to work correctly without special
exceptions. We choose to simply account for this memory in other_sys.
Let's talk about the exceptions. The first is the arenas array for
finding heap arena metadata from an address is mapped as read/write in
one large chunk. It's tens of MiB in size. On systems with demand
paging, we assume that the whole thing isn't paged in at once (after
all, it maps to the whole address space, and it's exceedingly difficult
with today's technology to even broach having as much physical memory as
the total address space). On systems where we have to commit memory
manually, we use a two-level structure.
Now, the reason why this is an exception is because we have no mechanism
to track what memory is paged in, and we can't just account for the
entire thing, because that would *look* like an enormous overhead.
Furthermore, this structure is on a few really, really critical paths in
the runtime, so doing more explicit tracking isn't really an option. So,
we explicitly don't and call sysAllocOS to map this memory.
The second exception is that we call sysFree with no accounting to clean
up address space reservations, or otherwise to throw out mappings we
don't care about. In this case, also drop down to a lower level and call
sysFreeOS to explicitly avoid accounting.
The third exception is debuglog allocations. That is purely a debugging
facility and ideally we want it to have as small an impact on the
runtime as possible. If we include it in mappedReady calculations, it
could cause GC pacing shifts in future CLs, especailly if one increases
the debuglog buffer sizes as a one-off.
As of this CL, these are the only three places in the runtime that would
pass nil for a stat to any of the functions in mem.go. As a result, this
CL makes sysMemStats mandatory to facilitate better accounting in the
future. It's now much easier to grep and find out where accounting is
explicitly elided, because one doesn't have to follow the trail of
sysMemStat nil pointer values, and can just look at the function name.
For #48409.
Change-Id: I274eb467fc2603881717482214fddc47c9eaf218
Reviewed-on: https://go-review.googlesource.com/c/go/+/393402
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
2022-03-15 02:48:18 +00:00
|
|
|
// sysStat must be non-nil.
|
|
|
|
|
//
|
2022-03-15 00:23:26 +00:00
|
|
|
// Don't split the stack as this function may be invoked without a valid G,
|
|
|
|
|
// which prevents us from allocating more stack.
|
|
|
|
|
//go:nosplit
|
|
|
|
|
func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer {
|
|
|
|
|
sysStat.add(int64(n))
|
2022-04-01 22:34:45 +00:00
|
|
|
gcController.mappedReady.Add(int64(n))
|
2022-03-15 00:23:26 +00:00
|
|
|
return sysAllocOS(n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sysUnused transitions a memory region from Ready to Prepared. It notifies the
|
|
|
|
|
// operating system that the physical pages backing this memory region are no
|
|
|
|
|
// longer needed and can be reused for other purposes. The contents of a
|
|
|
|
|
// sysUnused memory region are considered forfeit and the region must not be
|
|
|
|
|
// accessed again until sysUsed is called.
|
|
|
|
|
func sysUnused(v unsafe.Pointer, n uintptr) {
|
2022-04-01 22:34:45 +00:00
|
|
|
gcController.mappedReady.Add(-int64(n))
|
2022-03-15 00:23:26 +00:00
|
|
|
sysUnusedOS(v, n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sysUsed transitions a memory region from Prepared to Ready. It notifies the
|
|
|
|
|
// operating system that the memory region is needed and ensures that the region
|
|
|
|
|
// may be safely accessed. This is typically a no-op on systems that don't have
|
|
|
|
|
// an explicit commit step and hard over-commit limits, but is critical on
|
|
|
|
|
// Windows, for example.
|
runtime: track how much memory is mapped in the Ready state
This change adds a field to memstats called mappedReady that tracks how
much memory is in the Ready state at any given time. In essence, it's
the total memory usage by the Go runtime (with one exception which is
documented). Essentially, all memory mapped read/write that has either
been paged in or will soon.
To make tracking this not involve the many different stats that track
mapped memory, we track this statistic at a very low level. The downside
of tracking this statistic at such a low level is that it managed to
catch lots of situations where the runtime wasn't fully accounting for
memory. This change rectifies these situations by always accounting for
memory that's mapped in some way (i.e. always passing a sysMemStat to a
mem.go function), with *two* exceptions.
Rectifying these situations means also having the memory mapped during
testing being accounted for, so that tests (i.e. ReadMemStats) that
ultimately check mappedReady continue to work correctly without special
exceptions. We choose to simply account for this memory in other_sys.
Let's talk about the exceptions. The first is the arenas array for
finding heap arena metadata from an address is mapped as read/write in
one large chunk. It's tens of MiB in size. On systems with demand
paging, we assume that the whole thing isn't paged in at once (after
all, it maps to the whole address space, and it's exceedingly difficult
with today's technology to even broach having as much physical memory as
the total address space). On systems where we have to commit memory
manually, we use a two-level structure.
Now, the reason why this is an exception is because we have no mechanism
to track what memory is paged in, and we can't just account for the
entire thing, because that would *look* like an enormous overhead.
Furthermore, this structure is on a few really, really critical paths in
the runtime, so doing more explicit tracking isn't really an option. So,
we explicitly don't and call sysAllocOS to map this memory.
The second exception is that we call sysFree with no accounting to clean
up address space reservations, or otherwise to throw out mappings we
don't care about. In this case, also drop down to a lower level and call
sysFreeOS to explicitly avoid accounting.
The third exception is debuglog allocations. That is purely a debugging
facility and ideally we want it to have as small an impact on the
runtime as possible. If we include it in mappedReady calculations, it
could cause GC pacing shifts in future CLs, especailly if one increases
the debuglog buffer sizes as a one-off.
As of this CL, these are the only three places in the runtime that would
pass nil for a stat to any of the functions in mem.go. As a result, this
CL makes sysMemStats mandatory to facilitate better accounting in the
future. It's now much easier to grep and find out where accounting is
explicitly elided, because one doesn't have to follow the trail of
sysMemStat nil pointer values, and can just look at the function name.
For #48409.
Change-Id: I274eb467fc2603881717482214fddc47c9eaf218
Reviewed-on: https://go-review.googlesource.com/c/go/+/393402
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
2022-03-15 02:48:18 +00:00
|
|
|
//
|
|
|
|
|
// This operation is idempotent for memory already in the Prepared state, so
|
|
|
|
|
// it is safe to refer, with v and n, to a range of memory that includes both
|
2022-05-18 06:42:08 +10:00
|
|
|
// Prepared and Ready memory. However, the caller must provide the exact amount
|
runtime: track how much memory is mapped in the Ready state
This change adds a field to memstats called mappedReady that tracks how
much memory is in the Ready state at any given time. In essence, it's
the total memory usage by the Go runtime (with one exception which is
documented). Essentially, all memory mapped read/write that has either
been paged in or will soon.
To make tracking this not involve the many different stats that track
mapped memory, we track this statistic at a very low level. The downside
of tracking this statistic at such a low level is that it managed to
catch lots of situations where the runtime wasn't fully accounting for
memory. This change rectifies these situations by always accounting for
memory that's mapped in some way (i.e. always passing a sysMemStat to a
mem.go function), with *two* exceptions.
Rectifying these situations means also having the memory mapped during
testing being accounted for, so that tests (i.e. ReadMemStats) that
ultimately check mappedReady continue to work correctly without special
exceptions. We choose to simply account for this memory in other_sys.
Let's talk about the exceptions. The first is the arenas array for
finding heap arena metadata from an address is mapped as read/write in
one large chunk. It's tens of MiB in size. On systems with demand
paging, we assume that the whole thing isn't paged in at once (after
all, it maps to the whole address space, and it's exceedingly difficult
with today's technology to even broach having as much physical memory as
the total address space). On systems where we have to commit memory
manually, we use a two-level structure.
Now, the reason why this is an exception is because we have no mechanism
to track what memory is paged in, and we can't just account for the
entire thing, because that would *look* like an enormous overhead.
Furthermore, this structure is on a few really, really critical paths in
the runtime, so doing more explicit tracking isn't really an option. So,
we explicitly don't and call sysAllocOS to map this memory.
The second exception is that we call sysFree with no accounting to clean
up address space reservations, or otherwise to throw out mappings we
don't care about. In this case, also drop down to a lower level and call
sysFreeOS to explicitly avoid accounting.
The third exception is debuglog allocations. That is purely a debugging
facility and ideally we want it to have as small an impact on the
runtime as possible. If we include it in mappedReady calculations, it
could cause GC pacing shifts in future CLs, especailly if one increases
the debuglog buffer sizes as a one-off.
As of this CL, these are the only three places in the runtime that would
pass nil for a stat to any of the functions in mem.go. As a result, this
CL makes sysMemStats mandatory to facilitate better accounting in the
future. It's now much easier to grep and find out where accounting is
explicitly elided, because one doesn't have to follow the trail of
sysMemStat nil pointer values, and can just look at the function name.
For #48409.
Change-Id: I274eb467fc2603881717482214fddc47c9eaf218
Reviewed-on: https://go-review.googlesource.com/c/go/+/393402
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
2022-03-15 02:48:18 +00:00
|
|
|
// of Prepared memory for accounting purposes.
|
|
|
|
|
func sysUsed(v unsafe.Pointer, n, prepared uintptr) {
|
2022-04-01 22:34:45 +00:00
|
|
|
gcController.mappedReady.Add(int64(prepared))
|
2022-03-15 00:23:26 +00:00
|
|
|
sysUsedOS(v, n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sysHugePage does not transition memory regions, but instead provides a
|
|
|
|
|
// hint to the OS that it would be more efficient to back this memory region
|
|
|
|
|
// with pages of a larger size transparently.
|
|
|
|
|
func sysHugePage(v unsafe.Pointer, n uintptr) {
|
|
|
|
|
sysHugePageOS(v, n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sysFree transitions a memory region from any state to None. Therefore, it
|
|
|
|
|
// returns memory unconditionally. It is used if an out-of-memory error has been
|
|
|
|
|
// detected midway through an allocation or to carve out an aligned section of
|
|
|
|
|
// the address space. It is okay if sysFree is a no-op only if sysReserve always
|
|
|
|
|
// returns a memory region aligned to the heap allocator's alignment
|
|
|
|
|
// restrictions.
|
|
|
|
|
//
|
runtime: track how much memory is mapped in the Ready state
This change adds a field to memstats called mappedReady that tracks how
much memory is in the Ready state at any given time. In essence, it's
the total memory usage by the Go runtime (with one exception which is
documented). Essentially, all memory mapped read/write that has either
been paged in or will soon.
To make tracking this not involve the many different stats that track
mapped memory, we track this statistic at a very low level. The downside
of tracking this statistic at such a low level is that it managed to
catch lots of situations where the runtime wasn't fully accounting for
memory. This change rectifies these situations by always accounting for
memory that's mapped in some way (i.e. always passing a sysMemStat to a
mem.go function), with *two* exceptions.
Rectifying these situations means also having the memory mapped during
testing being accounted for, so that tests (i.e. ReadMemStats) that
ultimately check mappedReady continue to work correctly without special
exceptions. We choose to simply account for this memory in other_sys.
Let's talk about the exceptions. The first is the arenas array for
finding heap arena metadata from an address is mapped as read/write in
one large chunk. It's tens of MiB in size. On systems with demand
paging, we assume that the whole thing isn't paged in at once (after
all, it maps to the whole address space, and it's exceedingly difficult
with today's technology to even broach having as much physical memory as
the total address space). On systems where we have to commit memory
manually, we use a two-level structure.
Now, the reason why this is an exception is because we have no mechanism
to track what memory is paged in, and we can't just account for the
entire thing, because that would *look* like an enormous overhead.
Furthermore, this structure is on a few really, really critical paths in
the runtime, so doing more explicit tracking isn't really an option. So,
we explicitly don't and call sysAllocOS to map this memory.
The second exception is that we call sysFree with no accounting to clean
up address space reservations, or otherwise to throw out mappings we
don't care about. In this case, also drop down to a lower level and call
sysFreeOS to explicitly avoid accounting.
The third exception is debuglog allocations. That is purely a debugging
facility and ideally we want it to have as small an impact on the
runtime as possible. If we include it in mappedReady calculations, it
could cause GC pacing shifts in future CLs, especailly if one increases
the debuglog buffer sizes as a one-off.
As of this CL, these are the only three places in the runtime that would
pass nil for a stat to any of the functions in mem.go. As a result, this
CL makes sysMemStats mandatory to facilitate better accounting in the
future. It's now much easier to grep and find out where accounting is
explicitly elided, because one doesn't have to follow the trail of
sysMemStat nil pointer values, and can just look at the function name.
For #48409.
Change-Id: I274eb467fc2603881717482214fddc47c9eaf218
Reviewed-on: https://go-review.googlesource.com/c/go/+/393402
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
2022-03-15 02:48:18 +00:00
|
|
|
// sysStat must be non-nil.
|
|
|
|
|
//
|
2022-03-15 00:23:26 +00:00
|
|
|
// Don't split the stack as this function may be invoked without a valid G,
|
|
|
|
|
// which prevents us from allocating more stack.
|
|
|
|
|
//go:nosplit
|
|
|
|
|
func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) {
|
|
|
|
|
sysStat.add(-int64(n))
|
2022-04-01 22:34:45 +00:00
|
|
|
gcController.mappedReady.Add(-int64(n))
|
2022-03-15 00:23:26 +00:00
|
|
|
sysFreeOS(v, n)
|
|
|
|
|
}
|
|
|
|
|
|
runtime: track how much memory is mapped in the Ready state
This change adds a field to memstats called mappedReady that tracks how
much memory is in the Ready state at any given time. In essence, it's
the total memory usage by the Go runtime (with one exception which is
documented). Essentially, all memory mapped read/write that has either
been paged in or will soon.
To make tracking this not involve the many different stats that track
mapped memory, we track this statistic at a very low level. The downside
of tracking this statistic at such a low level is that it managed to
catch lots of situations where the runtime wasn't fully accounting for
memory. This change rectifies these situations by always accounting for
memory that's mapped in some way (i.e. always passing a sysMemStat to a
mem.go function), with *two* exceptions.
Rectifying these situations means also having the memory mapped during
testing being accounted for, so that tests (i.e. ReadMemStats) that
ultimately check mappedReady continue to work correctly without special
exceptions. We choose to simply account for this memory in other_sys.
Let's talk about the exceptions. The first is the arenas array for
finding heap arena metadata from an address is mapped as read/write in
one large chunk. It's tens of MiB in size. On systems with demand
paging, we assume that the whole thing isn't paged in at once (after
all, it maps to the whole address space, and it's exceedingly difficult
with today's technology to even broach having as much physical memory as
the total address space). On systems where we have to commit memory
manually, we use a two-level structure.
Now, the reason why this is an exception is because we have no mechanism
to track what memory is paged in, and we can't just account for the
entire thing, because that would *look* like an enormous overhead.
Furthermore, this structure is on a few really, really critical paths in
the runtime, so doing more explicit tracking isn't really an option. So,
we explicitly don't and call sysAllocOS to map this memory.
The second exception is that we call sysFree with no accounting to clean
up address space reservations, or otherwise to throw out mappings we
don't care about. In this case, also drop down to a lower level and call
sysFreeOS to explicitly avoid accounting.
The third exception is debuglog allocations. That is purely a debugging
facility and ideally we want it to have as small an impact on the
runtime as possible. If we include it in mappedReady calculations, it
could cause GC pacing shifts in future CLs, especailly if one increases
the debuglog buffer sizes as a one-off.
As of this CL, these are the only three places in the runtime that would
pass nil for a stat to any of the functions in mem.go. As a result, this
CL makes sysMemStats mandatory to facilitate better accounting in the
future. It's now much easier to grep and find out where accounting is
explicitly elided, because one doesn't have to follow the trail of
sysMemStat nil pointer values, and can just look at the function name.
For #48409.
Change-Id: I274eb467fc2603881717482214fddc47c9eaf218
Reviewed-on: https://go-review.googlesource.com/c/go/+/393402
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
2022-03-15 02:48:18 +00:00
|
|
|
// sysFault transitions a memory region from Ready to Reserved. It
|
2022-03-15 00:23:26 +00:00
|
|
|
// marks a region such that it will always fault if accessed. Used only for
|
|
|
|
|
// debugging the runtime.
|
runtime: track how much memory is mapped in the Ready state
This change adds a field to memstats called mappedReady that tracks how
much memory is in the Ready state at any given time. In essence, it's
the total memory usage by the Go runtime (with one exception which is
documented). Essentially, all memory mapped read/write that has either
been paged in or will soon.
To make tracking this not involve the many different stats that track
mapped memory, we track this statistic at a very low level. The downside
of tracking this statistic at such a low level is that it managed to
catch lots of situations where the runtime wasn't fully accounting for
memory. This change rectifies these situations by always accounting for
memory that's mapped in some way (i.e. always passing a sysMemStat to a
mem.go function), with *two* exceptions.
Rectifying these situations means also having the memory mapped during
testing being accounted for, so that tests (i.e. ReadMemStats) that
ultimately check mappedReady continue to work correctly without special
exceptions. We choose to simply account for this memory in other_sys.
Let's talk about the exceptions. The first is the arenas array for
finding heap arena metadata from an address is mapped as read/write in
one large chunk. It's tens of MiB in size. On systems with demand
paging, we assume that the whole thing isn't paged in at once (after
all, it maps to the whole address space, and it's exceedingly difficult
with today's technology to even broach having as much physical memory as
the total address space). On systems where we have to commit memory
manually, we use a two-level structure.
Now, the reason why this is an exception is because we have no mechanism
to track what memory is paged in, and we can't just account for the
entire thing, because that would *look* like an enormous overhead.
Furthermore, this structure is on a few really, really critical paths in
the runtime, so doing more explicit tracking isn't really an option. So,
we explicitly don't and call sysAllocOS to map this memory.
The second exception is that we call sysFree with no accounting to clean
up address space reservations, or otherwise to throw out mappings we
don't care about. In this case, also drop down to a lower level and call
sysFreeOS to explicitly avoid accounting.
The third exception is debuglog allocations. That is purely a debugging
facility and ideally we want it to have as small an impact on the
runtime as possible. If we include it in mappedReady calculations, it
could cause GC pacing shifts in future CLs, especailly if one increases
the debuglog buffer sizes as a one-off.
As of this CL, these are the only three places in the runtime that would
pass nil for a stat to any of the functions in mem.go. As a result, this
CL makes sysMemStats mandatory to facilitate better accounting in the
future. It's now much easier to grep and find out where accounting is
explicitly elided, because one doesn't have to follow the trail of
sysMemStat nil pointer values, and can just look at the function name.
For #48409.
Change-Id: I274eb467fc2603881717482214fddc47c9eaf218
Reviewed-on: https://go-review.googlesource.com/c/go/+/393402
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
2022-03-15 02:48:18 +00:00
|
|
|
//
|
|
|
|
|
// TODO(mknyszek): Currently it's true that all uses of sysFault transition
|
|
|
|
|
// memory from Ready to Reserved, but this may not be true in the future
|
|
|
|
|
// since on every platform the operation is much more general than that.
|
|
|
|
|
// If a transition from Prepared is ever introduced, create a new function
|
|
|
|
|
// that elides the Ready state accounting.
|
2022-03-15 00:23:26 +00:00
|
|
|
func sysFault(v unsafe.Pointer, n uintptr) {
|
2022-04-01 22:34:45 +00:00
|
|
|
gcController.mappedReady.Add(-int64(n))
|
2022-03-15 00:23:26 +00:00
|
|
|
sysFaultOS(v, n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sysReserve transitions a memory region from None to Reserved. It reserves
|
|
|
|
|
// address space in such a way that it would cause a fatal fault upon access
|
|
|
|
|
// (either via permissions or not committing the memory). Such a reservation is
|
|
|
|
|
// thus never backed by physical memory.
|
|
|
|
|
//
|
|
|
|
|
// If the pointer passed to it is non-nil, the caller wants the
|
|
|
|
|
// reservation there, but sysReserve can still choose another
|
|
|
|
|
// location if that one is unavailable.
|
|
|
|
|
//
|
|
|
|
|
// NOTE: sysReserve returns OS-aligned memory, but the heap allocator
|
|
|
|
|
// may use larger alignment, so the caller must be careful to realign the
|
|
|
|
|
// memory obtained by sysReserve.
|
|
|
|
|
func sysReserve(v unsafe.Pointer, n uintptr) unsafe.Pointer {
|
|
|
|
|
return sysReserveOS(v, n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sysMap transitions a memory region from Reserved to Prepared. It ensures the
|
|
|
|
|
// memory region can be efficiently transitioned to Ready.
|
runtime: track how much memory is mapped in the Ready state
This change adds a field to memstats called mappedReady that tracks how
much memory is in the Ready state at any given time. In essence, it's
the total memory usage by the Go runtime (with one exception which is
documented). Essentially, all memory mapped read/write that has either
been paged in or will soon.
To make tracking this not involve the many different stats that track
mapped memory, we track this statistic at a very low level. The downside
of tracking this statistic at such a low level is that it managed to
catch lots of situations where the runtime wasn't fully accounting for
memory. This change rectifies these situations by always accounting for
memory that's mapped in some way (i.e. always passing a sysMemStat to a
mem.go function), with *two* exceptions.
Rectifying these situations means also having the memory mapped during
testing being accounted for, so that tests (i.e. ReadMemStats) that
ultimately check mappedReady continue to work correctly without special
exceptions. We choose to simply account for this memory in other_sys.
Let's talk about the exceptions. The first is the arenas array for
finding heap arena metadata from an address is mapped as read/write in
one large chunk. It's tens of MiB in size. On systems with demand
paging, we assume that the whole thing isn't paged in at once (after
all, it maps to the whole address space, and it's exceedingly difficult
with today's technology to even broach having as much physical memory as
the total address space). On systems where we have to commit memory
manually, we use a two-level structure.
Now, the reason why this is an exception is because we have no mechanism
to track what memory is paged in, and we can't just account for the
entire thing, because that would *look* like an enormous overhead.
Furthermore, this structure is on a few really, really critical paths in
the runtime, so doing more explicit tracking isn't really an option. So,
we explicitly don't and call sysAllocOS to map this memory.
The second exception is that we call sysFree with no accounting to clean
up address space reservations, or otherwise to throw out mappings we
don't care about. In this case, also drop down to a lower level and call
sysFreeOS to explicitly avoid accounting.
The third exception is debuglog allocations. That is purely a debugging
facility and ideally we want it to have as small an impact on the
runtime as possible. If we include it in mappedReady calculations, it
could cause GC pacing shifts in future CLs, especailly if one increases
the debuglog buffer sizes as a one-off.
As of this CL, these are the only three places in the runtime that would
pass nil for a stat to any of the functions in mem.go. As a result, this
CL makes sysMemStats mandatory to facilitate better accounting in the
future. It's now much easier to grep and find out where accounting is
explicitly elided, because one doesn't have to follow the trail of
sysMemStat nil pointer values, and can just look at the function name.
For #48409.
Change-Id: I274eb467fc2603881717482214fddc47c9eaf218
Reviewed-on: https://go-review.googlesource.com/c/go/+/393402
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
2022-03-15 02:48:18 +00:00
|
|
|
//
|
|
|
|
|
// sysStat must be non-nil.
|
2022-03-15 00:23:26 +00:00
|
|
|
func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) {
|
|
|
|
|
sysStat.add(int64(n))
|
|
|
|
|
sysMapOS(v, n)
|
|
|
|
|
}
|