2014-11-11 17:05:02 -05:00
|
|
|
// Copyright 2009 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
|
|
|
|
|
|
2018-08-23 13:14:19 -04:00
|
|
|
import (
|
|
|
|
|
"runtime/internal/atomic"
|
|
|
|
|
"unsafe"
|
|
|
|
|
)
|
2014-11-11 17:05:02 -05:00
|
|
|
|
2015-02-19 13:38:46 -05:00
|
|
|
// Per-thread (in Go, per-P) cache for small objects.
|
2020-07-23 21:10:29 +00:00
|
|
|
// This includes a small object cache and local allocation stats.
|
2015-02-19 13:38:46 -05:00
|
|
|
// No locking needed because it is per-thread (per-P).
|
runtime: clear tiny alloc cache in mark term, not sweep term
The tiny alloc cache is maintained in a pointer from non-GC'd memory
(mcache) to heap memory and hence must be handled carefully.
Currently we clear the tiny alloc cache during sweep termination and,
if it is assigned to a non-nil value during concurrent marking, we
depend on a write barrier to keep the new value alive. However, while
the compiler currently always generates this write barrier, we're
treading on thin ice because write barriers may not happen for writes
to non-heap memory (e.g., typedmemmove). Without this lucky write
barrier, the GC may free a current tiny block while it's still
reachable by the tiny allocator, leading to later memory corruption.
Change this code so that, rather than depending on the write barrier,
we simply clear the tiny cache during mark termination when we're
clearing all of the other mcaches. If the current tiny block is
reachable from regular pointers, it will be retained; if it isn't
reachable from regular pointers, it may be freed, but that's okay
because there won't be any pointers in non-GC'd memory to it.
Change-Id: I8230980d8612c35c2997b9705641a1f9f865f879
Reviewed-on: https://go-review.googlesource.com/16962
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-11-16 15:20:59 -05:00
|
|
|
//
|
|
|
|
|
// mcaches are allocated from non-GC'd memory, so any heap pointers
|
|
|
|
|
// must be specially handled.
|
2016-10-11 22:58:21 -04:00
|
|
|
//
|
|
|
|
|
//go:notinheap
|
2015-02-19 13:38:46 -05:00
|
|
|
type mcache struct {
|
|
|
|
|
// The following members are accessed on every malloc,
|
|
|
|
|
// so they are grouped here for better caching.
|
2020-07-24 19:58:31 +00:00
|
|
|
nextSample uintptr // trigger heap sample after allocating this many bytes
|
|
|
|
|
scanAlloc uintptr // bytes of scannable heap allocated
|
runtime: clear tiny alloc cache in mark term, not sweep term
The tiny alloc cache is maintained in a pointer from non-GC'd memory
(mcache) to heap memory and hence must be handled carefully.
Currently we clear the tiny alloc cache during sweep termination and,
if it is assigned to a non-nil value during concurrent marking, we
depend on a write barrier to keep the new value alive. However, while
the compiler currently always generates this write barrier, we're
treading on thin ice because write barriers may not happen for writes
to non-heap memory (e.g., typedmemmove). Without this lucky write
barrier, the GC may free a current tiny block while it's still
reachable by the tiny allocator, leading to later memory corruption.
Change this code so that, rather than depending on the write barrier,
we simply clear the tiny cache during mark termination when we're
clearing all of the other mcaches. If the current tiny block is
reachable from regular pointers, it will be retained; if it isn't
reachable from regular pointers, it may be freed, but that's okay
because there won't be any pointers in non-GC'd memory to it.
Change-Id: I8230980d8612c35c2997b9705641a1f9f865f879
Reviewed-on: https://go-review.googlesource.com/16962
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-11-16 15:20:59 -05:00
|
|
|
|
2015-02-19 13:38:46 -05:00
|
|
|
// Allocator cache for tiny objects w/o pointers.
|
|
|
|
|
// See "Tiny allocator" comment in malloc.go.
|
runtime: clear tiny alloc cache in mark term, not sweep term
The tiny alloc cache is maintained in a pointer from non-GC'd memory
(mcache) to heap memory and hence must be handled carefully.
Currently we clear the tiny alloc cache during sweep termination and,
if it is assigned to a non-nil value during concurrent marking, we
depend on a write barrier to keep the new value alive. However, while
the compiler currently always generates this write barrier, we're
treading on thin ice because write barriers may not happen for writes
to non-heap memory (e.g., typedmemmove). Without this lucky write
barrier, the GC may free a current tiny block while it's still
reachable by the tiny allocator, leading to later memory corruption.
Change this code so that, rather than depending on the write barrier,
we simply clear the tiny cache during mark termination when we're
clearing all of the other mcaches. If the current tiny block is
reachable from regular pointers, it will be retained; if it isn't
reachable from regular pointers, it may be freed, but that's okay
because there won't be any pointers in non-GC'd memory to it.
Change-Id: I8230980d8612c35c2997b9705641a1f9f865f879
Reviewed-on: https://go-review.googlesource.com/16962
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-11-16 15:20:59 -05:00
|
|
|
|
|
|
|
|
// tiny points to the beginning of the current tiny block, or
|
|
|
|
|
// nil if there is no current tiny block.
|
|
|
|
|
//
|
|
|
|
|
// tiny is a heap pointer. Since mcache is in non-GC'd memory,
|
|
|
|
|
// we handle it by clearing it in releaseAll during mark
|
|
|
|
|
// termination.
|
2020-08-04 17:29:03 +00:00
|
|
|
//
|
|
|
|
|
// tinyAllocs is the number of tiny allocations performed
|
|
|
|
|
// by the P that owns this mcache.
|
2020-07-23 22:16:46 +00:00
|
|
|
tiny uintptr
|
|
|
|
|
tinyoffset uintptr
|
2020-08-04 17:29:03 +00:00
|
|
|
tinyAllocs uintptr
|
2015-02-19 13:38:46 -05:00
|
|
|
|
|
|
|
|
// The rest is not accessed on every malloc.
|
2016-02-09 17:53:07 -05:00
|
|
|
|
|
|
|
|
alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass
|
2015-02-19 13:38:46 -05:00
|
|
|
|
|
|
|
|
stackcache [_NumStackOrders]stackfreelist
|
|
|
|
|
|
2018-08-23 13:14:19 -04:00
|
|
|
// flushGen indicates the sweepgen during which this mcache
|
|
|
|
|
// was last flushed. If flushGen != mheap_.sweepgen, the spans
|
|
|
|
|
// in this mcache are stale and need to the flushed so they
|
|
|
|
|
// can be swept. This is done in acquirep.
|
|
|
|
|
flushGen uint32
|
2015-02-19 13:38:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A gclink is a node in a linked list of blocks, like mlink,
|
|
|
|
|
// but it is opaque to the garbage collector.
|
|
|
|
|
// The GC does not trace the pointers during collection,
|
|
|
|
|
// and the compiler does not emit write barriers for assignments
|
|
|
|
|
// of gclinkptr values. Code should store references to gclinks
|
|
|
|
|
// as gclinkptr, not as *gclink.
|
|
|
|
|
type gclink struct {
|
|
|
|
|
next gclinkptr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A gclinkptr is a pointer to a gclink, but it is opaque
|
|
|
|
|
// to the garbage collector.
|
|
|
|
|
type gclinkptr uintptr
|
|
|
|
|
|
|
|
|
|
// ptr returns the *gclink form of p.
|
|
|
|
|
// The result should be used for accessing fields, not stored
|
|
|
|
|
// in other data structures.
|
|
|
|
|
func (p gclinkptr) ptr() *gclink {
|
|
|
|
|
return (*gclink)(unsafe.Pointer(p))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type stackfreelist struct {
|
|
|
|
|
list gclinkptr // linked list of free stacks
|
|
|
|
|
size uintptr // total size of stacks in list
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-05 19:26:25 +00:00
|
|
|
// dummy mspan that contains no free objects.
|
2014-11-11 17:05:02 -05:00
|
|
|
var emptymspan mspan
|
|
|
|
|
|
|
|
|
|
func allocmcache() *mcache {
|
2019-05-17 14:48:04 +00:00
|
|
|
var c *mcache
|
|
|
|
|
systemstack(func() {
|
|
|
|
|
lock(&mheap_.lock)
|
|
|
|
|
c = (*mcache)(mheap_.cachealloc.alloc())
|
|
|
|
|
c.flushGen = mheap_.sweepgen
|
|
|
|
|
unlock(&mheap_.lock)
|
|
|
|
|
})
|
2016-02-09 17:53:07 -05:00
|
|
|
for i := range c.alloc {
|
2014-11-11 17:05:02 -05:00
|
|
|
c.alloc[i] = &emptymspan
|
|
|
|
|
}
|
2020-07-24 19:58:31 +00:00
|
|
|
c.nextSample = nextSample()
|
2014-11-11 17:05:02 -05:00
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-23 21:02:05 +00:00
|
|
|
// freemcache releases resources associated with this
|
|
|
|
|
// mcache and puts the object onto a free list.
|
|
|
|
|
//
|
|
|
|
|
// In some cases there is no way to simply release
|
|
|
|
|
// resources, such as statistics, so donate them to
|
|
|
|
|
// a different mcache (the recipient).
|
2020-08-04 17:29:03 +00:00
|
|
|
func freemcache(c *mcache) {
|
[dev.cc] runtime: delete scalararg, ptrarg; rename onM to systemstack
Scalararg and ptrarg are not "signal safe".
Go code filling them out can be interrupted by a signal,
and then the signal handler runs, and if it also ends up
in Go code that uses scalararg or ptrarg, now the old
values have been smashed.
For the pieces of code that do need to run in a signal handler,
we introduced onM_signalok, which is really just onM
except that the _signalok is meant to convey that the caller
asserts that scalarg and ptrarg will be restored to their old
values after the call (instead of the usual behavior, zeroing them).
Scalararg and ptrarg are also untyped and therefore error-prone.
Go code can always pass a closure instead of using scalararg
and ptrarg; they were only really necessary for C code.
And there's no more C code.
For all these reasons, delete scalararg and ptrarg, converting
the few remaining references to use closures.
Once those are gone, there is no need for a distinction between
onM and onM_signalok, so replace both with a single function
equivalent to the current onM_signalok (that is, it can be called
on any of the curg, g0, and gsignal stacks).
The name onM and the phrase 'm stack' are misnomers,
because on most system an M has two system stacks:
the main thread stack and the signal handling stack.
Correct the misnomer by naming the replacement function systemstack.
Fix a few references to "M stack" in code.
The main motivation for this change is to eliminate scalararg/ptrarg.
Rick and I have already seen them cause problems because
the calling sequence m.ptrarg[0] = p is a heap pointer assignment,
so it gets a write barrier. The write barrier also uses onM, so it has
all the same problems as if it were being invoked by a signal handler.
We worked around this by saving and restoring the old values
and by calling onM_signalok, but there's no point in keeping this nice
home for bugs around any longer.
This CL also changes funcline to return the file name as a result
instead of filling in a passed-in *string. (The *string signature is
left over from when the code was written in and called from C.)
That's arguably an unrelated change, except that once I had done
the ptrarg/scalararg/onM cleanup I started getting false positives
about the *string argument escaping (not allowed in package runtime).
The compiler is wrong, but the easiest fix is to write the code like
Go code instead of like C code. I am a bit worried that the compiler
is wrong because of some use of uninitialized memory in the escape
analysis. If that's the reason, it will go away when we convert the
compiler to Go. (And if not, we'll debug it the next time.)
LGTM=khr
R=r, khr
CC=austin, golang-codereviews, iant, rlh
https://golang.org/cl/174950043
2014-11-12 14:54:31 -05:00
|
|
|
systemstack(func() {
|
2015-11-11 16:13:51 -08:00
|
|
|
c.releaseAll()
|
2014-11-11 17:05:02 -05:00
|
|
|
stackcache_clear(c)
|
2014-11-15 08:00:38 -05:00
|
|
|
|
|
|
|
|
// NOTE(rsc,rlh): If gcworkbuffree comes back, we need to coordinate
|
|
|
|
|
// with the stealing of gcworkbufs during garbage collection to avoid
|
|
|
|
|
// a race where the workbuf is double-freed.
|
|
|
|
|
// gcworkbuffree(c.gcworkbuf)
|
|
|
|
|
|
2014-11-11 17:05:02 -05:00
|
|
|
lock(&mheap_.lock)
|
2015-11-11 16:13:51 -08:00
|
|
|
mheap_.cachealloc.free(unsafe.Pointer(c))
|
2014-11-11 17:05:02 -05:00
|
|
|
unlock(&mheap_.lock)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-03 20:08:25 +00:00
|
|
|
// getMCache is a convenience function which tries to obtain an mcache.
|
|
|
|
|
//
|
2020-11-02 16:58:38 +00:00
|
|
|
// Returns nil if we're not bootstrapping or we don't have a P. The caller's
|
|
|
|
|
// P must not change, so we must be in a non-preemptible state.
|
2021-09-11 20:53:24 +08:00
|
|
|
func getMCache(mp *m) *mcache {
|
2020-08-03 20:08:25 +00:00
|
|
|
// Grab the mcache, since that's where stats live.
|
2021-09-11 20:53:24 +08:00
|
|
|
pp := mp.p.ptr()
|
2020-08-03 20:08:25 +00:00
|
|
|
var c *mcache
|
|
|
|
|
if pp == nil {
|
|
|
|
|
// We will be called without a P while bootstrapping,
|
|
|
|
|
// in which case we use mcache0, which is set in mallocinit.
|
|
|
|
|
// mcache0 is cleared when bootstrapping is complete,
|
|
|
|
|
// by procresize.
|
|
|
|
|
c = mcache0
|
|
|
|
|
} else {
|
|
|
|
|
c = pp.mcache
|
|
|
|
|
}
|
|
|
|
|
return c
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-22 15:59:01 -04:00
|
|
|
// refill acquires a new span of span class spc for c. This span will
|
|
|
|
|
// have at least one free object. The current span in c must be full.
|
|
|
|
|
//
|
|
|
|
|
// Must run in a non-preemptible context since otherwise the owner of
|
|
|
|
|
// c could change.
|
2017-10-09 10:54:22 +01:00
|
|
|
func (c *mcache) refill(spc spanClass) {
|
2014-11-11 17:05:02 -05:00
|
|
|
// Return the current cached span to the central lists.
|
2016-02-09 17:53:07 -05:00
|
|
|
s := c.alloc[spc]
|
2016-02-11 13:57:58 -05:00
|
|
|
|
2016-02-16 17:16:43 -05:00
|
|
|
if uintptr(s.allocCount) != s.nelems {
|
2016-02-11 13:57:58 -05:00
|
|
|
throw("refill of span with free space remaining")
|
2014-11-11 17:05:02 -05:00
|
|
|
}
|
|
|
|
|
if s != &emptymspan {
|
2018-08-23 13:14:19 -04:00
|
|
|
// Mark this span as no longer cached.
|
|
|
|
|
if s.sweepgen != mheap_.sweepgen+3 {
|
|
|
|
|
throw("bad sweepgen in refill")
|
|
|
|
|
}
|
2020-02-19 16:37:48 +00:00
|
|
|
mheap_.central[spc].mcentral.uncacheSpan(s)
|
2022-01-10 22:59:26 +00:00
|
|
|
|
|
|
|
|
// Count up how many slots were used and record it.
|
|
|
|
|
stats := memstats.heapStats.acquire()
|
2022-05-03 19:28:25 +00:00
|
|
|
slotsUsed := int64(s.allocCount) - int64(s.allocCountBeforeCache)
|
|
|
|
|
atomic.Xadd64(&stats.smallAllocCount[spc.sizeclass()], slotsUsed)
|
2022-01-10 22:59:26 +00:00
|
|
|
|
|
|
|
|
// Flush tinyAllocs.
|
|
|
|
|
if spc == tinySpanClass {
|
2022-05-03 19:28:25 +00:00
|
|
|
atomic.Xadd64(&stats.tinyAllocCount, int64(c.tinyAllocs))
|
2022-01-10 22:59:26 +00:00
|
|
|
c.tinyAllocs = 0
|
|
|
|
|
}
|
|
|
|
|
memstats.heapStats.release()
|
|
|
|
|
|
2022-03-19 21:46:12 +00:00
|
|
|
// Count the allocs in inconsistent, internal stats.
|
2022-05-03 19:28:25 +00:00
|
|
|
bytesAllocated := slotsUsed * int64(s.elemsize)
|
2022-04-01 22:34:45 +00:00
|
|
|
gcController.totalAlloc.Add(bytesAllocated)
|
2022-03-19 21:46:12 +00:00
|
|
|
|
2022-01-10 22:59:26 +00:00
|
|
|
// Clear the second allocCount just to be safe.
|
|
|
|
|
s.allocCountBeforeCache = 0
|
2014-11-11 17:05:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get a new cached span from the central lists.
|
2016-02-09 17:53:07 -05:00
|
|
|
s = mheap_.central[spc].mcentral.cacheSpan()
|
2014-11-11 17:05:02 -05:00
|
|
|
if s == nil {
|
2014-12-27 20:58:00 -08:00
|
|
|
throw("out of memory")
|
2014-11-11 17:05:02 -05:00
|
|
|
}
|
2016-02-11 13:57:58 -05:00
|
|
|
|
2016-02-16 17:16:43 -05:00
|
|
|
if uintptr(s.allocCount) == s.nelems {
|
2016-02-11 13:57:58 -05:00
|
|
|
throw("span has no free space")
|
2014-11-11 17:05:02 -05:00
|
|
|
}
|
2016-02-11 13:57:58 -05:00
|
|
|
|
2018-08-23 13:14:19 -04:00
|
|
|
// Indicate that this span is cached and prevent asynchronous
|
|
|
|
|
// sweeping in the next sweep phase.
|
|
|
|
|
s.sweepgen = mheap_.sweepgen + 3
|
|
|
|
|
|
2022-01-10 22:59:26 +00:00
|
|
|
// Store the current alloc count for accounting later.
|
|
|
|
|
s.allocCountBeforeCache = s.allocCount
|
runtime: flush local_scan directly and more often
Now that local_scan is the last mcache-based statistic that is flushed
by purgecachedstats, and heap_scan and gcController.revise may be
interacted with concurrently, we don't need to flush heap_scan at
arbitrary locations where the heap is locked, and we don't need
purgecachedstats and cachestats anymore. Instead, we can flush
local_scan at the same time we update heap_live in refill, so the two
updates may share the same revise call.
Clean up unused functions, remove code that would cause the heap to get
locked in the allocSpan when it didn't need to (other than to flush
local_scan), and flush local_scan explicitly in a few important places.
Notably we need to flush local_scan whenever we flush the other stats,
but it doesn't need to be donated anywhere, so have releaseAll do the
flushing. Also, we need to flush local_scan before we set heap_scan at
the end of a GC, which was previously handled by cachestats. Just do so
explicitly -- it's not much code and it becomes a lot more clear why we
need to do so.
Change-Id: I35ac081784df7744d515479896a41d530653692d
Reviewed-on: https://go-review.googlesource.com/c/go/+/246968
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Trust: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Pratt <mpratt@google.com>
2020-07-23 22:36:58 +00:00
|
|
|
|
2022-07-07 20:01:21 +00:00
|
|
|
// Update heapLive and flush scanAlloc.
|
|
|
|
|
//
|
|
|
|
|
// We have not yet allocated anything new into the span, but we
|
|
|
|
|
// assume that all of its slots will get used, so this makes
|
|
|
|
|
// heapLive an overestimate.
|
|
|
|
|
//
|
|
|
|
|
// When the span gets uncached, we'll fix up this overestimate
|
|
|
|
|
// if necessary (see releaseAll).
|
|
|
|
|
//
|
|
|
|
|
// We pick an overestimate here because an underestimate leads
|
|
|
|
|
// the pacer to believe that it's in better shape than it is,
|
|
|
|
|
// which appears to lead to more memory used. See #53738 for
|
|
|
|
|
// more details.
|
|
|
|
|
usedBytes := uintptr(s.allocCount) * s.elemsize
|
|
|
|
|
gcController.update(int64(s.npages*pageSize)-int64(usedBytes), int64(c.scanAlloc))
|
|
|
|
|
c.scanAlloc = 0
|
|
|
|
|
|
2016-02-09 17:53:07 -05:00
|
|
|
c.alloc[spc] = s
|
2014-11-11 17:05:02 -05:00
|
|
|
}
|
|
|
|
|
|
2020-07-24 19:58:31 +00:00
|
|
|
// allocLarge allocates a span for a large object.
|
runtime: clean up allocation zeroing
Currently, the runtime zeroes allocations in several ways. First, small
object spans are always zeroed if they come from mheap, and their slots
are zeroed later in mallocgc if needed. Second, large object spans
(objects that have their own spans) plumb the need for zeroing down into
mheap. Thirdly, large objects that have no pointers have their zeroing
delayed until after preemption is reenabled, but before returning in
mallocgc.
All of this has two consequences:
1. Spans for small objects that come from mheap are sometimes
unnecessarily zeroed, even if the mallocgc call that created them
doesn't need the object slot to be zeroed.
2. This is all messy and difficult to reason about.
This CL simplifies this code, resolving both (1) and (2). First, it
recognizes that zeroing in mheap is unnecessary for small object spans;
mallocgc and its callees in mcache and mcentral, by design, are *always*
able to deal with non-zeroed spans. They must, for they deal with
recycled spans all the time. Once this fact is made clear, the only
remaining use of zeroing in mheap is for large objects.
As a result, this CL lifts mheap zeroing for large objects into
mallocgc, to parallel all the other codepaths in mallocgc. This is makes
the large object allocation code less surprising.
Next, this CL sets the flag for the delayed zeroing explicitly in the one
case where it matters, and inverts and renames the flag from isZeroed to
delayZeroing.
Finally, it adds a check to make sure that only pointer-free allocations
take the delayed zeroing codepath, as an extra safety measure.
Benchmark results: https://perf.golang.org/search?q=upload:20211028.8
Inspired by tapir.liu@gmail.com's CL 343470.
Change-Id: I7e1296adc19ce8a02c8d93a0a5082aefb2673e8f
Reviewed-on: https://go-review.googlesource.com/c/go/+/359477
Trust: Michael Knyszek <mknyszek@google.com>
Reviewed-by: David Chase <drchase@google.com>
2021-10-28 17:52:22 +00:00
|
|
|
func (c *mcache) allocLarge(size uintptr, noscan bool) *mspan {
|
2020-07-23 21:10:29 +00:00
|
|
|
if size+_PageSize < size {
|
|
|
|
|
throw("out of memory")
|
|
|
|
|
}
|
|
|
|
|
npages := size >> _PageShift
|
|
|
|
|
if size&_PageMask != 0 {
|
|
|
|
|
npages++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Deduct credit for this span allocation and sweep if
|
|
|
|
|
// necessary. mHeap_Alloc will also sweep npages, so this only
|
|
|
|
|
// pays the debt down to npage pages.
|
|
|
|
|
deductSweepCredit(npages*_PageSize, npages)
|
|
|
|
|
|
|
|
|
|
spc := makeSpanClass(0, noscan)
|
runtime: clean up allocation zeroing
Currently, the runtime zeroes allocations in several ways. First, small
object spans are always zeroed if they come from mheap, and their slots
are zeroed later in mallocgc if needed. Second, large object spans
(objects that have their own spans) plumb the need for zeroing down into
mheap. Thirdly, large objects that have no pointers have their zeroing
delayed until after preemption is reenabled, but before returning in
mallocgc.
All of this has two consequences:
1. Spans for small objects that come from mheap are sometimes
unnecessarily zeroed, even if the mallocgc call that created them
doesn't need the object slot to be zeroed.
2. This is all messy and difficult to reason about.
This CL simplifies this code, resolving both (1) and (2). First, it
recognizes that zeroing in mheap is unnecessary for small object spans;
mallocgc and its callees in mcache and mcentral, by design, are *always*
able to deal with non-zeroed spans. They must, for they deal with
recycled spans all the time. Once this fact is made clear, the only
remaining use of zeroing in mheap is for large objects.
As a result, this CL lifts mheap zeroing for large objects into
mallocgc, to parallel all the other codepaths in mallocgc. This is makes
the large object allocation code less surprising.
Next, this CL sets the flag for the delayed zeroing explicitly in the one
case where it matters, and inverts and renames the flag from isZeroed to
delayZeroing.
Finally, it adds a check to make sure that only pointer-free allocations
take the delayed zeroing codepath, as an extra safety measure.
Benchmark results: https://perf.golang.org/search?q=upload:20211028.8
Inspired by tapir.liu@gmail.com's CL 343470.
Change-Id: I7e1296adc19ce8a02c8d93a0a5082aefb2673e8f
Reviewed-on: https://go-review.googlesource.com/c/go/+/359477
Trust: Michael Knyszek <mknyszek@google.com>
Reviewed-by: David Chase <drchase@google.com>
2021-10-28 17:52:22 +00:00
|
|
|
s := mheap_.alloc(npages, spc)
|
2020-07-23 21:10:29 +00:00
|
|
|
if s == nil {
|
|
|
|
|
throw("out of memory")
|
|
|
|
|
}
|
2022-03-19 21:46:12 +00:00
|
|
|
|
|
|
|
|
// Count the alloc in consistent, external stats.
|
2020-11-02 19:03:16 +00:00
|
|
|
stats := memstats.heapStats.acquire()
|
2022-05-03 19:28:25 +00:00
|
|
|
atomic.Xadd64(&stats.largeAlloc, int64(npages*pageSize))
|
|
|
|
|
atomic.Xadd64(&stats.largeAllocCount, 1)
|
2020-11-02 19:03:16 +00:00
|
|
|
memstats.heapStats.release()
|
2020-07-23 21:10:29 +00:00
|
|
|
|
2022-03-19 21:46:12 +00:00
|
|
|
// Count the alloc in inconsistent, internal stats.
|
2022-04-01 22:34:45 +00:00
|
|
|
gcController.totalAlloc.Add(int64(npages * pageSize))
|
2022-03-19 21:46:12 +00:00
|
|
|
|
2021-04-11 18:29:03 +00:00
|
|
|
// Update heapLive.
|
|
|
|
|
gcController.update(int64(s.npages*pageSize), 0)
|
2020-07-23 21:10:29 +00:00
|
|
|
|
|
|
|
|
// Put the large span in the mcentral swept list so that it's
|
|
|
|
|
// visible to the background sweeper.
|
|
|
|
|
mheap_.central[spc].mcentral.fullSwept(mheap_.sweepgen).push(s)
|
|
|
|
|
s.limit = s.base() + size
|
2022-08-09 15:44:48 +00:00
|
|
|
heapBitsForAddr(s.base()).initSpan(s)
|
runtime: clean up allocation zeroing
Currently, the runtime zeroes allocations in several ways. First, small
object spans are always zeroed if they come from mheap, and their slots
are zeroed later in mallocgc if needed. Second, large object spans
(objects that have their own spans) plumb the need for zeroing down into
mheap. Thirdly, large objects that have no pointers have their zeroing
delayed until after preemption is reenabled, but before returning in
mallocgc.
All of this has two consequences:
1. Spans for small objects that come from mheap are sometimes
unnecessarily zeroed, even if the mallocgc call that created them
doesn't need the object slot to be zeroed.
2. This is all messy and difficult to reason about.
This CL simplifies this code, resolving both (1) and (2). First, it
recognizes that zeroing in mheap is unnecessary for small object spans;
mallocgc and its callees in mcache and mcentral, by design, are *always*
able to deal with non-zeroed spans. They must, for they deal with
recycled spans all the time. Once this fact is made clear, the only
remaining use of zeroing in mheap is for large objects.
As a result, this CL lifts mheap zeroing for large objects into
mallocgc, to parallel all the other codepaths in mallocgc. This is makes
the large object allocation code less surprising.
Next, this CL sets the flag for the delayed zeroing explicitly in the one
case where it matters, and inverts and renames the flag from isZeroed to
delayZeroing.
Finally, it adds a check to make sure that only pointer-free allocations
take the delayed zeroing codepath, as an extra safety measure.
Benchmark results: https://perf.golang.org/search?q=upload:20211028.8
Inspired by tapir.liu@gmail.com's CL 343470.
Change-Id: I7e1296adc19ce8a02c8d93a0a5082aefb2673e8f
Reviewed-on: https://go-review.googlesource.com/c/go/+/359477
Trust: Michael Knyszek <mknyszek@google.com>
Reviewed-by: David Chase <drchase@google.com>
2021-10-28 17:52:22 +00:00
|
|
|
return s
|
2020-07-23 21:10:29 +00:00
|
|
|
}
|
|
|
|
|
|
2015-11-11 16:13:51 -08:00
|
|
|
func (c *mcache) releaseAll() {
|
2020-07-24 19:58:31 +00:00
|
|
|
// Take this opportunity to flush scanAlloc.
|
2021-04-11 18:29:03 +00:00
|
|
|
scanAlloc := int64(c.scanAlloc)
|
2020-07-24 19:58:31 +00:00
|
|
|
c.scanAlloc = 0
|
runtime: flush local_scan directly and more often
Now that local_scan is the last mcache-based statistic that is flushed
by purgecachedstats, and heap_scan and gcController.revise may be
interacted with concurrently, we don't need to flush heap_scan at
arbitrary locations where the heap is locked, and we don't need
purgecachedstats and cachestats anymore. Instead, we can flush
local_scan at the same time we update heap_live in refill, so the two
updates may share the same revise call.
Clean up unused functions, remove code that would cause the heap to get
locked in the allocSpan when it didn't need to (other than to flush
local_scan), and flush local_scan explicitly in a few important places.
Notably we need to flush local_scan whenever we flush the other stats,
but it doesn't need to be donated anywhere, so have releaseAll do the
flushing. Also, we need to flush local_scan before we set heap_scan at
the end of a GC, which was previously handled by cachestats. Just do so
explicitly -- it's not much code and it becomes a lot more clear why we
need to do so.
Change-Id: I35ac081784df7744d515479896a41d530653692d
Reviewed-on: https://go-review.googlesource.com/c/go/+/246968
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Trust: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Pratt <mpratt@google.com>
2020-07-23 22:36:58 +00:00
|
|
|
|
2022-07-07 20:01:21 +00:00
|
|
|
sg := mheap_.sweepgen
|
|
|
|
|
dHeapLive := int64(0)
|
2016-02-09 17:53:07 -05:00
|
|
|
for i := range c.alloc {
|
2014-11-11 17:05:02 -05:00
|
|
|
s := c.alloc[i]
|
|
|
|
|
if s != &emptymspan {
|
2022-05-03 19:28:25 +00:00
|
|
|
slotsUsed := int64(s.allocCount) - int64(s.allocCountBeforeCache)
|
2022-03-19 21:46:12 +00:00
|
|
|
s.allocCountBeforeCache = 0
|
|
|
|
|
|
2022-01-10 22:59:26 +00:00
|
|
|
// Adjust smallAllocCount for whatever was allocated.
|
2020-11-02 19:03:16 +00:00
|
|
|
stats := memstats.heapStats.acquire()
|
2022-05-03 19:28:25 +00:00
|
|
|
atomic.Xadd64(&stats.smallAllocCount[spanClass(i).sizeclass()], slotsUsed)
|
2020-11-02 19:03:16 +00:00
|
|
|
memstats.heapStats.release()
|
2022-03-19 21:46:12 +00:00
|
|
|
|
|
|
|
|
// Adjust the actual allocs in inconsistent, internal stats.
|
|
|
|
|
// We assumed earlier that the full span gets allocated.
|
2022-05-03 19:28:25 +00:00
|
|
|
gcController.totalAlloc.Add(slotsUsed * int64(s.elemsize))
|
2022-01-10 22:59:26 +00:00
|
|
|
|
2022-07-07 20:01:21 +00:00
|
|
|
if s.sweepgen != sg+1 {
|
|
|
|
|
// refill conservatively counted unallocated slots in gcController.heapLive.
|
|
|
|
|
// Undo this.
|
|
|
|
|
//
|
|
|
|
|
// If this span was cached before sweep, then gcController.heapLive was totally
|
|
|
|
|
// recomputed since caching this span, so we don't do this for stale spans.
|
|
|
|
|
dHeapLive -= int64(uintptr(s.nelems)-uintptr(s.allocCount)) * int64(s.elemsize)
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-23 22:07:44 +00:00
|
|
|
// Release the span to the mcentral.
|
2015-11-11 16:13:51 -08:00
|
|
|
mheap_.central[i].mcentral.uncacheSpan(s)
|
2014-11-11 17:05:02 -05:00
|
|
|
c.alloc[i] = &emptymspan
|
|
|
|
|
}
|
|
|
|
|
}
|
runtime: clear tiny alloc cache in mark term, not sweep term
The tiny alloc cache is maintained in a pointer from non-GC'd memory
(mcache) to heap memory and hence must be handled carefully.
Currently we clear the tiny alloc cache during sweep termination and,
if it is assigned to a non-nil value during concurrent marking, we
depend on a write barrier to keep the new value alive. However, while
the compiler currently always generates this write barrier, we're
treading on thin ice because write barriers may not happen for writes
to non-heap memory (e.g., typedmemmove). Without this lucky write
barrier, the GC may free a current tiny block while it's still
reachable by the tiny allocator, leading to later memory corruption.
Change this code so that, rather than depending on the write barrier,
we simply clear the tiny cache during mark termination when we're
clearing all of the other mcaches. If the current tiny block is
reachable from regular pointers, it will be retained; if it isn't
reachable from regular pointers, it may be freed, but that's okay
because there won't be any pointers in non-GC'd memory to it.
Change-Id: I8230980d8612c35c2997b9705641a1f9f865f879
Reviewed-on: https://go-review.googlesource.com/16962
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-11-16 15:20:59 -05:00
|
|
|
// Clear tinyalloc pool.
|
2015-11-16 15:31:50 -05:00
|
|
|
c.tiny = 0
|
runtime: clear tiny alloc cache in mark term, not sweep term
The tiny alloc cache is maintained in a pointer from non-GC'd memory
(mcache) to heap memory and hence must be handled carefully.
Currently we clear the tiny alloc cache during sweep termination and,
if it is assigned to a non-nil value during concurrent marking, we
depend on a write barrier to keep the new value alive. However, while
the compiler currently always generates this write barrier, we're
treading on thin ice because write barriers may not happen for writes
to non-heap memory (e.g., typedmemmove). Without this lucky write
barrier, the GC may free a current tiny block while it's still
reachable by the tiny allocator, leading to later memory corruption.
Change this code so that, rather than depending on the write barrier,
we simply clear the tiny cache during mark termination when we're
clearing all of the other mcaches. If the current tiny block is
reachable from regular pointers, it will be retained; if it isn't
reachable from regular pointers, it may be freed, but that's okay
because there won't be any pointers in non-GC'd memory to it.
Change-Id: I8230980d8612c35c2997b9705641a1f9f865f879
Reviewed-on: https://go-review.googlesource.com/16962
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2015-11-16 15:20:59 -05:00
|
|
|
c.tinyoffset = 0
|
2021-04-21 23:50:58 +00:00
|
|
|
|
|
|
|
|
// Flush tinyAllocs.
|
|
|
|
|
stats := memstats.heapStats.acquire()
|
2022-05-03 19:28:25 +00:00
|
|
|
atomic.Xadd64(&stats.tinyAllocCount, int64(c.tinyAllocs))
|
2020-08-04 17:29:03 +00:00
|
|
|
c.tinyAllocs = 0
|
2021-04-21 23:50:58 +00:00
|
|
|
memstats.heapStats.release()
|
runtime: flush local_scan directly and more often
Now that local_scan is the last mcache-based statistic that is flushed
by purgecachedstats, and heap_scan and gcController.revise may be
interacted with concurrently, we don't need to flush heap_scan at
arbitrary locations where the heap is locked, and we don't need
purgecachedstats and cachestats anymore. Instead, we can flush
local_scan at the same time we update heap_live in refill, so the two
updates may share the same revise call.
Clean up unused functions, remove code that would cause the heap to get
locked in the allocSpan when it didn't need to (other than to flush
local_scan), and flush local_scan explicitly in a few important places.
Notably we need to flush local_scan whenever we flush the other stats,
but it doesn't need to be donated anywhere, so have releaseAll do the
flushing. Also, we need to flush local_scan before we set heap_scan at
the end of a GC, which was previously handled by cachestats. Just do so
explicitly -- it's not much code and it becomes a lot more clear why we
need to do so.
Change-Id: I35ac081784df7744d515479896a41d530653692d
Reviewed-on: https://go-review.googlesource.com/c/go/+/246968
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Trust: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Pratt <mpratt@google.com>
2020-07-23 22:36:58 +00:00
|
|
|
|
2022-07-07 20:01:21 +00:00
|
|
|
// Update heapLive and heapScan.
|
|
|
|
|
gcController.update(dHeapLive, scanAlloc)
|
2014-11-11 17:05:02 -05:00
|
|
|
}
|
2018-08-23 13:14:19 -04:00
|
|
|
|
|
|
|
|
// prepareForSweep flushes c if the system has entered a new sweep phase
|
|
|
|
|
// since c was populated. This must happen between the sweep phase
|
|
|
|
|
// starting and the first allocation from c.
|
|
|
|
|
func (c *mcache) prepareForSweep() {
|
|
|
|
|
// Alternatively, instead of making sure we do this on every P
|
|
|
|
|
// between starting the world and allocating on that P, we
|
|
|
|
|
// could leave allocate-black on, allow allocation to continue
|
|
|
|
|
// as usual, use a ragged barrier at the beginning of sweep to
|
|
|
|
|
// ensure all cached spans are swept, and then disable
|
|
|
|
|
// allocate-black. However, with this approach it's difficult
|
|
|
|
|
// to avoid spilling mark bits into the *next* GC cycle.
|
|
|
|
|
sg := mheap_.sweepgen
|
|
|
|
|
if c.flushGen == sg {
|
|
|
|
|
return
|
|
|
|
|
} else if c.flushGen != sg-2 {
|
|
|
|
|
println("bad flushGen", c.flushGen, "in prepareForSweep; sweepgen", sg)
|
|
|
|
|
throw("bad flushGen")
|
|
|
|
|
}
|
|
|
|
|
c.releaseAll()
|
|
|
|
|
stackcache_clear(c)
|
|
|
|
|
atomic.Store(&c.flushGen, mheap_.sweepgen) // Synchronizes with gcStart
|
|
|
|
|
}
|