Source file src/runtime/mcache.go
1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime 6 7 import ( 8 "internal/runtime/atomic" 9 "runtime/internal/sys" 10 "unsafe" 11 ) 12 13 // Per-thread (in Go, per-P) cache for small objects. 14 // This includes a small object cache and local allocation stats. 15 // No locking needed because it is per-thread (per-P). 16 // 17 // mcaches are allocated from non-GC'd memory, so any heap pointers 18 // must be specially handled. 19 type mcache struct { 20 _ sys.NotInHeap 21 22 // The following members are accessed on every malloc, 23 // so they are grouped here for better caching. 24 nextSample uintptr // trigger heap sample after allocating this many bytes 25 scanAlloc uintptr // bytes of scannable heap allocated 26 27 // Allocator cache for tiny objects w/o pointers. 28 // See "Tiny allocator" comment in malloc.go. 29 30 // tiny points to the beginning of the current tiny block, or 31 // nil if there is no current tiny block. 32 // 33 // tiny is a heap pointer. Since mcache is in non-GC'd memory, 34 // we handle it by clearing it in releaseAll during mark 35 // termination. 36 // 37 // tinyAllocs is the number of tiny allocations performed 38 // by the P that owns this mcache. 39 tiny uintptr 40 tinyoffset uintptr 41 tinyAllocs uintptr 42 43 // The rest is not accessed on every malloc. 44 45 alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass 46 47 stackcache [_NumStackOrders]stackfreelist 48 49 // flushGen indicates the sweepgen during which this mcache 50 // was last flushed. If flushGen != mheap_.sweepgen, the spans 51 // in this mcache are stale and need to the flushed so they 52 // can be swept. This is done in acquirep. 53 flushGen atomic.Uint32 54 } 55 56 // A gclink is a node in a linked list of blocks, like mlink, 57 // but it is opaque to the garbage collector. 58 // The GC does not trace the pointers during collection, 59 // and the compiler does not emit write barriers for assignments 60 // of gclinkptr values. Code should store references to gclinks 61 // as gclinkptr, not as *gclink. 62 type gclink struct { 63 next gclinkptr 64 } 65 66 // A gclinkptr is a pointer to a gclink, but it is opaque 67 // to the garbage collector. 68 type gclinkptr uintptr 69 70 // ptr returns the *gclink form of p. 71 // The result should be used for accessing fields, not stored 72 // in other data structures. 73 func (p gclinkptr) ptr() *gclink { 74 return (*gclink)(unsafe.Pointer(p)) 75 } 76 77 type stackfreelist struct { 78 list gclinkptr // linked list of free stacks 79 size uintptr // total size of stacks in list 80 } 81 82 // dummy mspan that contains no free objects. 83 var emptymspan mspan 84 85 func allocmcache() *mcache { 86 var c *mcache 87 systemstack(func() { 88 lock(&mheap_.lock) 89 c = (*mcache)(mheap_.cachealloc.alloc()) 90 c.flushGen.Store(mheap_.sweepgen) 91 unlock(&mheap_.lock) 92 }) 93 for i := range c.alloc { 94 c.alloc[i] = &emptymspan 95 } 96 c.nextSample = nextSample() 97 return c 98 } 99 100 // freemcache releases resources associated with this 101 // mcache and puts the object onto a free list. 102 // 103 // In some cases there is no way to simply release 104 // resources, such as statistics, so donate them to 105 // a different mcache (the recipient). 106 func freemcache(c *mcache) { 107 systemstack(func() { 108 c.releaseAll() 109 stackcache_clear(c) 110 111 // NOTE(rsc,rlh): If gcworkbuffree comes back, we need to coordinate 112 // with the stealing of gcworkbufs during garbage collection to avoid 113 // a race where the workbuf is double-freed. 114 // gcworkbuffree(c.gcworkbuf) 115 116 lock(&mheap_.lock) 117 mheap_.cachealloc.free(unsafe.Pointer(c)) 118 unlock(&mheap_.lock) 119 }) 120 } 121 122 // getMCache is a convenience function which tries to obtain an mcache. 123 // 124 // Returns nil if we're not bootstrapping or we don't have a P. The caller's 125 // P must not change, so we must be in a non-preemptible state. 126 func getMCache(mp *m) *mcache { 127 // Grab the mcache, since that's where stats live. 128 pp := mp.p.ptr() 129 var c *mcache 130 if pp == nil { 131 // We will be called without a P while bootstrapping, 132 // in which case we use mcache0, which is set in mallocinit. 133 // mcache0 is cleared when bootstrapping is complete, 134 // by procresize. 135 c = mcache0 136 } else { 137 c = pp.mcache 138 } 139 return c 140 } 141 142 // refill acquires a new span of span class spc for c. This span will 143 // have at least one free object. The current span in c must be full. 144 // 145 // Must run in a non-preemptible context since otherwise the owner of 146 // c could change. 147 func (c *mcache) refill(spc spanClass) { 148 // Return the current cached span to the central lists. 149 s := c.alloc[spc] 150 151 if s.allocCount != s.nelems { 152 throw("refill of span with free space remaining") 153 } 154 if s != &emptymspan { 155 // Mark this span as no longer cached. 156 if s.sweepgen != mheap_.sweepgen+3 { 157 throw("bad sweepgen in refill") 158 } 159 mheap_.central[spc].mcentral.uncacheSpan(s) 160 161 // Count up how many slots were used and record it. 162 stats := memstats.heapStats.acquire() 163 slotsUsed := int64(s.allocCount) - int64(s.allocCountBeforeCache) 164 atomic.Xadd64(&stats.smallAllocCount[spc.sizeclass()], slotsUsed) 165 166 // Flush tinyAllocs. 167 if spc == tinySpanClass { 168 atomic.Xadd64(&stats.tinyAllocCount, int64(c.tinyAllocs)) 169 c.tinyAllocs = 0 170 } 171 memstats.heapStats.release() 172 173 // Count the allocs in inconsistent, internal stats. 174 bytesAllocated := slotsUsed * int64(s.elemsize) 175 gcController.totalAlloc.Add(bytesAllocated) 176 177 // Clear the second allocCount just to be safe. 178 s.allocCountBeforeCache = 0 179 } 180 181 // Get a new cached span from the central lists. 182 s = mheap_.central[spc].mcentral.cacheSpan() 183 if s == nil { 184 throw("out of memory") 185 } 186 187 if s.allocCount == s.nelems { 188 throw("span has no free space") 189 } 190 191 // Indicate that this span is cached and prevent asynchronous 192 // sweeping in the next sweep phase. 193 s.sweepgen = mheap_.sweepgen + 3 194 195 // Store the current alloc count for accounting later. 196 s.allocCountBeforeCache = s.allocCount 197 198 // Update heapLive and flush scanAlloc. 199 // 200 // We have not yet allocated anything new into the span, but we 201 // assume that all of its slots will get used, so this makes 202 // heapLive an overestimate. 203 // 204 // When the span gets uncached, we'll fix up this overestimate 205 // if necessary (see releaseAll). 206 // 207 // We pick an overestimate here because an underestimate leads 208 // the pacer to believe that it's in better shape than it is, 209 // which appears to lead to more memory used. See #53738 for 210 // more details. 211 usedBytes := uintptr(s.allocCount) * s.elemsize 212 gcController.update(int64(s.npages*pageSize)-int64(usedBytes), int64(c.scanAlloc)) 213 c.scanAlloc = 0 214 215 c.alloc[spc] = s 216 } 217 218 // allocLarge allocates a span for a large object. 219 func (c *mcache) allocLarge(size uintptr, noscan bool) *mspan { 220 if size+_PageSize < size { 221 throw("out of memory") 222 } 223 npages := size >> _PageShift 224 if size&_PageMask != 0 { 225 npages++ 226 } 227 228 // Deduct credit for this span allocation and sweep if 229 // necessary. mHeap_Alloc will also sweep npages, so this only 230 // pays the debt down to npage pages. 231 deductSweepCredit(npages*_PageSize, npages) 232 233 spc := makeSpanClass(0, noscan) 234 s := mheap_.alloc(npages, spc) 235 if s == nil { 236 throw("out of memory") 237 } 238 239 // Count the alloc in consistent, external stats. 240 stats := memstats.heapStats.acquire() 241 atomic.Xadd64(&stats.largeAlloc, int64(npages*pageSize)) 242 atomic.Xadd64(&stats.largeAllocCount, 1) 243 memstats.heapStats.release() 244 245 // Count the alloc in inconsistent, internal stats. 246 gcController.totalAlloc.Add(int64(npages * pageSize)) 247 248 // Update heapLive. 249 gcController.update(int64(s.npages*pageSize), 0) 250 251 // Put the large span in the mcentral swept list so that it's 252 // visible to the background sweeper. 253 mheap_.central[spc].mcentral.fullSwept(mheap_.sweepgen).push(s) 254 s.limit = s.base() + size 255 s.initHeapBits(false) 256 return s 257 } 258 259 func (c *mcache) releaseAll() { 260 // Take this opportunity to flush scanAlloc. 261 scanAlloc := int64(c.scanAlloc) 262 c.scanAlloc = 0 263 264 sg := mheap_.sweepgen 265 dHeapLive := int64(0) 266 for i := range c.alloc { 267 s := c.alloc[i] 268 if s != &emptymspan { 269 slotsUsed := int64(s.allocCount) - int64(s.allocCountBeforeCache) 270 s.allocCountBeforeCache = 0 271 272 // Adjust smallAllocCount for whatever was allocated. 273 stats := memstats.heapStats.acquire() 274 atomic.Xadd64(&stats.smallAllocCount[spanClass(i).sizeclass()], slotsUsed) 275 memstats.heapStats.release() 276 277 // Adjust the actual allocs in inconsistent, internal stats. 278 // We assumed earlier that the full span gets allocated. 279 gcController.totalAlloc.Add(slotsUsed * int64(s.elemsize)) 280 281 if s.sweepgen != sg+1 { 282 // refill conservatively counted unallocated slots in gcController.heapLive. 283 // Undo this. 284 // 285 // If this span was cached before sweep, then gcController.heapLive was totally 286 // recomputed since caching this span, so we don't do this for stale spans. 287 dHeapLive -= int64(s.nelems-s.allocCount) * int64(s.elemsize) 288 } 289 290 // Release the span to the mcentral. 291 mheap_.central[i].mcentral.uncacheSpan(s) 292 c.alloc[i] = &emptymspan 293 } 294 } 295 // Clear tinyalloc pool. 296 c.tiny = 0 297 c.tinyoffset = 0 298 299 // Flush tinyAllocs. 300 stats := memstats.heapStats.acquire() 301 atomic.Xadd64(&stats.tinyAllocCount, int64(c.tinyAllocs)) 302 c.tinyAllocs = 0 303 memstats.heapStats.release() 304 305 // Update heapLive and heapScan. 306 gcController.update(dHeapLive, scanAlloc) 307 } 308 309 // prepareForSweep flushes c if the system has entered a new sweep phase 310 // since c was populated. This must happen between the sweep phase 311 // starting and the first allocation from c. 312 func (c *mcache) prepareForSweep() { 313 // Alternatively, instead of making sure we do this on every P 314 // between starting the world and allocating on that P, we 315 // could leave allocate-black on, allow allocation to continue 316 // as usual, use a ragged barrier at the beginning of sweep to 317 // ensure all cached spans are swept, and then disable 318 // allocate-black. However, with this approach it's difficult 319 // to avoid spilling mark bits into the *next* GC cycle. 320 sg := mheap_.sweepgen 321 flushGen := c.flushGen.Load() 322 if flushGen == sg { 323 return 324 } else if flushGen != sg-2 { 325 println("bad flushGen", flushGen, "in prepareForSweep; sweepgen", sg) 326 throw("bad flushGen") 327 } 328 c.releaseAll() 329 stackcache_clear(c) 330 c.flushGen.Store(mheap_.sweepgen) // Synchronizes with gcStart 331 } 332