github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/arena.go (about) 1 // Copyright 2022 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 // Implementation of (safe) user arenas. 6 // 7 // This file contains the implementation of user arenas wherein Go values can 8 // be manually allocated and freed in bulk. The act of manually freeing memory, 9 // potentially before a GC cycle, means that a garbage collection cycle can be 10 // delayed, improving efficiency by reducing GC cycle frequency. There are other 11 // potential efficiency benefits, such as improved locality and access to a more 12 // efficient allocation strategy. 13 // 14 // What makes the arenas here safe is that once they are freed, accessing the 15 // arena's memory will cause an explicit program fault, and the arena's address 16 // space will not be reused until no more pointers into it are found. There's one 17 // exception to this: if an arena allocated memory that isn't exhausted, it's placed 18 // back into a pool for reuse. This means that a crash is not always guaranteed. 19 // 20 // While this may seem unsafe, it still prevents memory corruption, and is in fact 21 // necessary in order to make new(T) a valid implementation of arenas. Such a property 22 // is desirable to allow for a trivial implementation. (It also avoids complexities 23 // that arise from synchronization with the GC when trying to set the arena chunks to 24 // fault while the GC is active.) 25 // 26 // The implementation works in layers. At the bottom, arenas are managed in chunks. 27 // Each chunk must be a multiple of the heap arena size, or the heap arena size must 28 // be divisible by the arena chunks. The address space for each chunk, and each 29 // corresponding heapArena for that address space, are eternally reserved for use as 30 // arena chunks. That is, they can never be used for the general heap. Each chunk 31 // is also represented by a single mspan, and is modeled as a single large heap 32 // allocation. It must be, because each chunk contains ordinary Go values that may 33 // point into the heap, so it must be scanned just like any other object. Any 34 // pointer into a chunk will therefore always cause the whole chunk to be scanned 35 // while its corresponding arena is still live. 36 // 37 // Chunks may be allocated either from new memory mapped by the OS on our behalf, 38 // or by reusing old freed chunks. When chunks are freed, their underlying memory 39 // is returned to the OS, set to fault on access, and may not be reused until the 40 // program doesn't point into the chunk anymore (the code refers to this state as 41 // "quarantined"), a property checked by the GC. 42 // 43 // The sweeper handles moving chunks out of this quarantine state to be ready for 44 // reuse. When the chunk is placed into the quarantine state, its corresponding 45 // span is marked as noscan so that the GC doesn't try to scan memory that would 46 // cause a fault. 47 // 48 // At the next layer are the user arenas themselves. They consist of a single 49 // active chunk which new Go values are bump-allocated into and a list of chunks 50 // that were exhausted when allocating into the arena. Once the arena is freed, 51 // it frees all full chunks it references, and places the active one onto a reuse 52 // list for a future arena to use. Each arena keeps its list of referenced chunks 53 // explicitly live until it is freed. Each user arena also maps to an object which 54 // has a finalizer attached that ensures the arena's chunks are all freed even if 55 // the arena itself is never explicitly freed. 56 // 57 // Pointer-ful memory is bump-allocated from low addresses to high addresses in each 58 // chunk, while pointer-free memory is bump-allocated from high address to low 59 // addresses. The reason for this is to take advantage of a GC optimization wherein 60 // the GC will stop scanning an object when there are no more pointers in it, which 61 // also allows us to elide clearing the heap bitmap for pointer-free Go values 62 // allocated into arenas. 63 // 64 // Note that arenas are not safe to use concurrently. 65 // 66 // In summary, there are 2 resources: arenas, and arena chunks. They exist in the 67 // following lifecycle: 68 // 69 // (1) A new arena is created via newArena. 70 // (2) Chunks are allocated to hold memory allocated into the arena with new or slice. 71 // (a) Chunks are first allocated from the reuse list of partially-used chunks. 72 // (b) If there are no such chunks, then chunks on the ready list are taken. 73 // (c) Failing all the above, memory for a new chunk is mapped. 74 // (3) The arena is freed, or all references to it are dropped, triggering its finalizer. 75 // (a) If the GC is not active, exhausted chunks are set to fault and placed on a 76 // quarantine list. 77 // (b) If the GC is active, exhausted chunks are placed on a fault list and will 78 // go through step (a) at a later point in time. 79 // (c) Any remaining partially-used chunk is placed on a reuse list. 80 // (4) Once no more pointers are found into quarantined arena chunks, the sweeper 81 // takes these chunks out of quarantine and places them on the ready list. 82 83 package runtime 84 85 import ( 86 "internal/goarch" 87 "runtime/internal/atomic" 88 "runtime/internal/math" 89 "unsafe" 90 ) 91 92 // Functions starting with arena_ are meant to be exported to downstream users 93 // of arenas. They should wrap these functions in a higher-lever API. 94 // 95 // The underlying arena and its resources are managed through an opaque unsafe.Pointer. 96 97 // arena_newArena is a wrapper around newUserArena. 98 // 99 //go:linkname arena_newArena arena.runtime_arena_newArena 100 func arena_newArena() unsafe.Pointer { 101 return unsafe.Pointer(newUserArena()) 102 } 103 104 // arena_arena_New is a wrapper around (*userArena).new, except that typ 105 // is an any (must be a *_type, still) and typ must be a type descriptor 106 // for a pointer to the type to actually be allocated, i.e. pass a *T 107 // to allocate a T. This is necessary because this function returns a *T. 108 // 109 //go:linkname arena_arena_New arena.runtime_arena_arena_New 110 func arena_arena_New(arena unsafe.Pointer, typ any) any { 111 t := (*_type)(efaceOf(&typ).data) 112 if t.Kind_&kindMask != kindPtr { 113 throw("arena_New: non-pointer type") 114 } 115 te := (*ptrtype)(unsafe.Pointer(t)).Elem 116 x := ((*userArena)(arena)).new(te) 117 var result any 118 e := efaceOf(&result) 119 e._type = t 120 e.data = x 121 return result 122 } 123 124 // arena_arena_Slice is a wrapper around (*userArena).slice. 125 // 126 //go:linkname arena_arena_Slice arena.runtime_arena_arena_Slice 127 func arena_arena_Slice(arena unsafe.Pointer, slice any, cap int) { 128 ((*userArena)(arena)).slice(slice, cap) 129 } 130 131 // arena_arena_Free is a wrapper around (*userArena).free. 132 // 133 //go:linkname arena_arena_Free arena.runtime_arena_arena_Free 134 func arena_arena_Free(arena unsafe.Pointer) { 135 ((*userArena)(arena)).free() 136 } 137 138 // arena_heapify takes a value that lives in an arena and makes a copy 139 // of it on the heap. Values that don't live in an arena are returned unmodified. 140 // 141 //go:linkname arena_heapify arena.runtime_arena_heapify 142 func arena_heapify(s any) any { 143 var v unsafe.Pointer 144 e := efaceOf(&s) 145 t := e._type 146 switch t.Kind_ & kindMask { 147 case kindString: 148 v = stringStructOf((*string)(e.data)).str 149 case kindSlice: 150 v = (*slice)(e.data).array 151 case kindPtr: 152 v = e.data 153 default: 154 panic("arena: Clone only supports pointers, slices, and strings") 155 } 156 span := spanOf(uintptr(v)) 157 if span == nil || !span.isUserArenaChunk { 158 // Not stored in a user arena chunk. 159 return s 160 } 161 // Heap-allocate storage for a copy. 162 var x any 163 switch t.Kind_ & kindMask { 164 case kindString: 165 s1 := s.(string) 166 s2, b := rawstring(len(s1)) 167 copy(b, s1) 168 x = s2 169 case kindSlice: 170 len := (*slice)(e.data).len 171 et := (*slicetype)(unsafe.Pointer(t)).Elem 172 sl := new(slice) 173 *sl = slice{makeslicecopy(et, len, len, (*slice)(e.data).array), len, len} 174 xe := efaceOf(&x) 175 xe._type = t 176 xe.data = unsafe.Pointer(sl) 177 case kindPtr: 178 et := (*ptrtype)(unsafe.Pointer(t)).Elem 179 e2 := newobject(et) 180 typedmemmove(et, e2, e.data) 181 xe := efaceOf(&x) 182 xe._type = t 183 xe.data = e2 184 } 185 return x 186 } 187 188 const ( 189 // userArenaChunkBytes is the size of a user arena chunk. 190 userArenaChunkBytesMax = 8 << 20 191 userArenaChunkBytes = uintptr(int64(userArenaChunkBytesMax-heapArenaBytes)&(int64(userArenaChunkBytesMax-heapArenaBytes)>>63) + heapArenaBytes) // min(userArenaChunkBytesMax, heapArenaBytes) 192 193 // userArenaChunkPages is the number of pages a user arena chunk uses. 194 userArenaChunkPages = userArenaChunkBytes / pageSize 195 196 // userArenaChunkMaxAllocBytes is the maximum size of an object that can 197 // be allocated from an arena. This number is chosen to cap worst-case 198 // fragmentation of user arenas to 25%. Larger allocations are redirected 199 // to the heap. 200 userArenaChunkMaxAllocBytes = userArenaChunkBytes / 4 201 ) 202 203 func init() { 204 if userArenaChunkPages*pageSize != userArenaChunkBytes { 205 throw("user arena chunk size is not a multiple of the page size") 206 } 207 if userArenaChunkBytes%physPageSize != 0 { 208 throw("user arena chunk size is not a multiple of the physical page size") 209 } 210 if userArenaChunkBytes < heapArenaBytes { 211 if heapArenaBytes%userArenaChunkBytes != 0 { 212 throw("user arena chunk size is smaller than a heap arena, but doesn't divide it") 213 } 214 } else { 215 if userArenaChunkBytes%heapArenaBytes != 0 { 216 throw("user arena chunks size is larger than a heap arena, but not a multiple") 217 } 218 } 219 lockInit(&userArenaState.lock, lockRankUserArenaState) 220 } 221 222 type userArena struct { 223 // full is a list of full chunks that have not enough free memory left, and 224 // that we'll free once this user arena is freed. 225 // 226 // Can't use mSpanList here because it's not-in-heap. 227 fullList *mspan 228 229 // active is the user arena chunk we're currently allocating into. 230 active *mspan 231 232 // refs is a set of references to the arena chunks so that they're kept alive. 233 // 234 // The last reference in the list always refers to active, while the rest of 235 // them correspond to fullList. Specifically, the head of fullList is the 236 // second-to-last one, fullList.next is the third-to-last, and so on. 237 // 238 // In other words, every time a new chunk becomes active, its appended to this 239 // list. 240 refs []unsafe.Pointer 241 242 // defunct is true if free has been called on this arena. 243 // 244 // This is just a best-effort way to discover a concurrent allocation 245 // and free. Also used to detect a double-free. 246 defunct atomic.Bool 247 } 248 249 // newUserArena creates a new userArena ready to be used. 250 func newUserArena() *userArena { 251 a := new(userArena) 252 SetFinalizer(a, func(a *userArena) { 253 // If arena handle is dropped without being freed, then call 254 // free on the arena, so the arena chunks are never reclaimed 255 // by the garbage collector. 256 a.free() 257 }) 258 a.refill() 259 return a 260 } 261 262 // new allocates a new object of the provided type into the arena, and returns 263 // its pointer. 264 // 265 // This operation is not safe to call concurrently with other operations on the 266 // same arena. 267 func (a *userArena) new(typ *_type) unsafe.Pointer { 268 return a.alloc(typ, -1) 269 } 270 271 // slice allocates a new slice backing store. slice must be a pointer to a slice 272 // (i.e. *[]T), because userArenaSlice will update the slice directly. 273 // 274 // cap determines the capacity of the slice backing store and must be non-negative. 275 // 276 // This operation is not safe to call concurrently with other operations on the 277 // same arena. 278 func (a *userArena) slice(sl any, cap int) { 279 if cap < 0 { 280 panic("userArena.slice: negative cap") 281 } 282 i := efaceOf(&sl) 283 typ := i._type 284 if typ.Kind_&kindMask != kindPtr { 285 panic("slice result of non-ptr type") 286 } 287 typ = (*ptrtype)(unsafe.Pointer(typ)).Elem 288 if typ.Kind_&kindMask != kindSlice { 289 panic("slice of non-ptr-to-slice type") 290 } 291 typ = (*slicetype)(unsafe.Pointer(typ)).Elem 292 // t is now the element type of the slice we want to allocate. 293 294 *((*slice)(i.data)) = slice{a.alloc(typ, cap), cap, cap} 295 } 296 297 // free returns the userArena's chunks back to mheap and marks it as defunct. 298 // 299 // Must be called at most once for any given arena. 300 // 301 // This operation is not safe to call concurrently with other operations on the 302 // same arena. 303 func (a *userArena) free() { 304 // Check for a double-free. 305 if a.defunct.Load() { 306 panic("arena double free") 307 } 308 309 // Mark ourselves as defunct. 310 a.defunct.Store(true) 311 SetFinalizer(a, nil) 312 313 // Free all the full arenas. 314 // 315 // The refs on this list are in reverse order from the second-to-last. 316 s := a.fullList 317 i := len(a.refs) - 2 318 for s != nil { 319 a.fullList = s.next 320 s.next = nil 321 freeUserArenaChunk(s, a.refs[i]) 322 s = a.fullList 323 i-- 324 } 325 if a.fullList != nil || i >= 0 { 326 // There's still something left on the full list, or we 327 // failed to actually iterate over the entire refs list. 328 throw("full list doesn't match refs list in length") 329 } 330 331 // Put the active chunk onto the reuse list. 332 // 333 // Note that active's reference is always the last reference in refs. 334 s = a.active 335 if s != nil { 336 if raceenabled || msanenabled || asanenabled { 337 // Don't reuse arenas with sanitizers enabled. We want to catch 338 // any use-after-free errors aggressively. 339 freeUserArenaChunk(s, a.refs[len(a.refs)-1]) 340 } else { 341 lock(&userArenaState.lock) 342 userArenaState.reuse = append(userArenaState.reuse, liveUserArenaChunk{s, a.refs[len(a.refs)-1]}) 343 unlock(&userArenaState.lock) 344 } 345 } 346 // nil out a.active so that a race with freeing will more likely cause a crash. 347 a.active = nil 348 a.refs = nil 349 } 350 351 // alloc reserves space in the current chunk or calls refill and reserves space 352 // in a new chunk. If cap is negative, the type will be taken literally, otherwise 353 // it will be considered as an element type for a slice backing store with capacity 354 // cap. 355 func (a *userArena) alloc(typ *_type, cap int) unsafe.Pointer { 356 s := a.active 357 var x unsafe.Pointer 358 for { 359 x = s.userArenaNextFree(typ, cap) 360 if x != nil { 361 break 362 } 363 s = a.refill() 364 } 365 return x 366 } 367 368 // refill inserts the current arena chunk onto the full list and obtains a new 369 // one, either from the partial list or allocating a new one, both from mheap. 370 func (a *userArena) refill() *mspan { 371 // If there's an active chunk, assume it's full. 372 s := a.active 373 if s != nil { 374 if s.userArenaChunkFree.size() > userArenaChunkMaxAllocBytes { 375 // It's difficult to tell when we're actually out of memory 376 // in a chunk because the allocation that failed may still leave 377 // some free space available. However, that amount of free space 378 // should never exceed the maximum allocation size. 379 throw("wasted too much memory in an arena chunk") 380 } 381 s.next = a.fullList 382 a.fullList = s 383 a.active = nil 384 s = nil 385 } 386 var x unsafe.Pointer 387 388 // Check the partially-used list. 389 lock(&userArenaState.lock) 390 if len(userArenaState.reuse) > 0 { 391 // Pick off the last arena chunk from the list. 392 n := len(userArenaState.reuse) - 1 393 x = userArenaState.reuse[n].x 394 s = userArenaState.reuse[n].mspan 395 userArenaState.reuse[n].x = nil 396 userArenaState.reuse[n].mspan = nil 397 userArenaState.reuse = userArenaState.reuse[:n] 398 } 399 unlock(&userArenaState.lock) 400 if s == nil { 401 // Allocate a new one. 402 x, s = newUserArenaChunk() 403 if s == nil { 404 throw("out of memory") 405 } 406 } 407 a.refs = append(a.refs, x) 408 a.active = s 409 return s 410 } 411 412 type liveUserArenaChunk struct { 413 *mspan // Must represent a user arena chunk. 414 415 // Reference to mspan.base() to keep the chunk alive. 416 x unsafe.Pointer 417 } 418 419 var userArenaState struct { 420 lock mutex 421 422 // reuse contains a list of partially-used and already-live 423 // user arena chunks that can be quickly reused for another 424 // arena. 425 // 426 // Protected by lock. 427 reuse []liveUserArenaChunk 428 429 // fault contains full user arena chunks that need to be faulted. 430 // 431 // Protected by lock. 432 fault []liveUserArenaChunk 433 } 434 435 // userArenaNextFree reserves space in the user arena for an item of the specified 436 // type. If cap is not -1, this is for an array of cap elements of type t. 437 func (s *mspan) userArenaNextFree(typ *_type, cap int) unsafe.Pointer { 438 size := typ.Size_ 439 if cap > 0 { 440 if size > ^uintptr(0)/uintptr(cap) { 441 // Overflow. 442 throw("out of memory") 443 } 444 size *= uintptr(cap) 445 } 446 if size == 0 || cap == 0 { 447 return unsafe.Pointer(&zerobase) 448 } 449 if size > userArenaChunkMaxAllocBytes { 450 // Redirect allocations that don't fit into a chunk well directly 451 // from the heap. 452 if cap >= 0 { 453 return newarray(typ, cap) 454 } 455 return newobject(typ) 456 } 457 458 // Prevent preemption as we set up the space for a new object. 459 // 460 // Act like we're allocating. 461 mp := acquirem() 462 if mp.mallocing != 0 { 463 throw("malloc deadlock") 464 } 465 if mp.gsignal == getg() { 466 throw("malloc during signal") 467 } 468 mp.mallocing = 1 469 470 var ptr unsafe.Pointer 471 if typ.PtrBytes == 0 { 472 // Allocate pointer-less objects from the tail end of the chunk. 473 v, ok := s.userArenaChunkFree.takeFromBack(size, typ.Align_) 474 if ok { 475 ptr = unsafe.Pointer(v) 476 } 477 } else { 478 v, ok := s.userArenaChunkFree.takeFromFront(size, typ.Align_) 479 if ok { 480 ptr = unsafe.Pointer(v) 481 } 482 } 483 if ptr == nil { 484 // Failed to allocate. 485 mp.mallocing = 0 486 releasem(mp) 487 return nil 488 } 489 if s.needzero != 0 { 490 throw("arena chunk needs zeroing, but should already be zeroed") 491 } 492 // Set up heap bitmap and do extra accounting. 493 if typ.PtrBytes != 0 { 494 if cap >= 0 { 495 userArenaHeapBitsSetSliceType(typ, cap, ptr, s.base()) 496 } else { 497 userArenaHeapBitsSetType(typ, ptr, s.base()) 498 } 499 c := getMCache(mp) 500 if c == nil { 501 throw("mallocgc called without a P or outside bootstrapping") 502 } 503 if cap > 0 { 504 c.scanAlloc += size - (typ.Size_ - typ.PtrBytes) 505 } else { 506 c.scanAlloc += typ.PtrBytes 507 } 508 } 509 510 // Ensure that the stores above that initialize x to 511 // type-safe memory and set the heap bits occur before 512 // the caller can make ptr observable to the garbage 513 // collector. Otherwise, on weakly ordered machines, 514 // the garbage collector could follow a pointer to x, 515 // but see uninitialized memory or stale heap bits. 516 publicationBarrier() 517 518 mp.mallocing = 0 519 releasem(mp) 520 521 return ptr 522 } 523 524 // userArenaHeapBitsSetType is the equivalent of heapBitsSetType but for 525 // non-slice-backing-store Go values allocated in a user arena chunk. It 526 // sets up the heap bitmap for the value with type typ allocated at address ptr. 527 // base is the base address of the arena chunk. 528 func userArenaHeapBitsSetType(typ *_type, ptr unsafe.Pointer, base uintptr) { 529 h := writeHeapBitsForAddr(uintptr(ptr)) 530 531 // Our last allocation might have ended right at a noMorePtrs mark, 532 // which we would not have erased. We need to erase that mark here, 533 // because we're going to start adding new heap bitmap bits. 534 // We only need to clear one mark, because below we make sure to 535 // pad out the bits with zeroes and only write one noMorePtrs bit 536 // for each new object. 537 // (This is only necessary at noMorePtrs boundaries, as noMorePtrs 538 // marks within an object allocated with newAt will be erased by 539 // the normal writeHeapBitsForAddr mechanism.) 540 // 541 // Note that we skip this if this is the first allocation in the 542 // arena because there's definitely no previous noMorePtrs mark 543 // (in fact, we *must* do this, because we're going to try to back 544 // up a pointer to fix this up). 545 if uintptr(ptr)%(8*goarch.PtrSize*goarch.PtrSize) == 0 && uintptr(ptr) != base { 546 // Back up one pointer and rewrite that pointer. That will 547 // cause the writeHeapBits implementation to clear the 548 // noMorePtrs bit we need to clear. 549 r := heapBitsForAddr(uintptr(ptr)-goarch.PtrSize, goarch.PtrSize) 550 _, p := r.next() 551 b := uintptr(0) 552 if p == uintptr(ptr)-goarch.PtrSize { 553 b = 1 554 } 555 h = writeHeapBitsForAddr(uintptr(ptr) - goarch.PtrSize) 556 h = h.write(b, 1) 557 } 558 559 p := typ.GCData // start of 1-bit pointer mask (or GC program) 560 var gcProgBits uintptr 561 if typ.Kind_&kindGCProg != 0 { 562 // Expand gc program, using the object itself for storage. 563 gcProgBits = runGCProg(addb(p, 4), (*byte)(ptr)) 564 p = (*byte)(ptr) 565 } 566 nb := typ.PtrBytes / goarch.PtrSize 567 568 for i := uintptr(0); i < nb; i += ptrBits { 569 k := nb - i 570 if k > ptrBits { 571 k = ptrBits 572 } 573 h = h.write(readUintptr(addb(p, i/8)), k) 574 } 575 // Note: we call pad here to ensure we emit explicit 0 bits 576 // for the pointerless tail of the object. This ensures that 577 // there's only a single noMorePtrs mark for the next object 578 // to clear. We don't need to do this to clear stale noMorePtrs 579 // markers from previous uses because arena chunk pointer bitmaps 580 // are always fully cleared when reused. 581 h = h.pad(typ.Size_ - typ.PtrBytes) 582 h.flush(uintptr(ptr), typ.Size_) 583 584 if typ.Kind_&kindGCProg != 0 { 585 // Zero out temporary ptrmask buffer inside object. 586 memclrNoHeapPointers(ptr, (gcProgBits+7)/8) 587 } 588 589 // Double-check that the bitmap was written out correctly. 590 // 591 // Derived from heapBitsSetType. 592 const doubleCheck = false 593 if doubleCheck { 594 size := typ.Size_ 595 x := uintptr(ptr) 596 h := heapBitsForAddr(x, size) 597 for i := uintptr(0); i < size; i += goarch.PtrSize { 598 // Compute the pointer bit we want at offset i. 599 want := false 600 off := i % typ.Size_ 601 if off < typ.PtrBytes { 602 j := off / goarch.PtrSize 603 want = *addb(typ.GCData, j/8)>>(j%8)&1 != 0 604 } 605 if want { 606 var addr uintptr 607 h, addr = h.next() 608 if addr != x+i { 609 throw("userArenaHeapBitsSetType: pointer entry not correct") 610 } 611 } 612 } 613 if _, addr := h.next(); addr != 0 { 614 throw("userArenaHeapBitsSetType: extra pointer") 615 } 616 } 617 } 618 619 // userArenaHeapBitsSetSliceType is the equivalent of heapBitsSetType but for 620 // Go slice backing store values allocated in a user arena chunk. It sets up the 621 // heap bitmap for n consecutive values with type typ allocated at address ptr. 622 func userArenaHeapBitsSetSliceType(typ *_type, n int, ptr unsafe.Pointer, base uintptr) { 623 mem, overflow := math.MulUintptr(typ.Size_, uintptr(n)) 624 if overflow || n < 0 || mem > maxAlloc { 625 panic(plainError("runtime: allocation size out of range")) 626 } 627 for i := 0; i < n; i++ { 628 userArenaHeapBitsSetType(typ, add(ptr, uintptr(i)*typ.Size_), base) 629 } 630 } 631 632 // newUserArenaChunk allocates a user arena chunk, which maps to a single 633 // heap arena and single span. Returns a pointer to the base of the chunk 634 // (this is really important: we need to keep the chunk alive) and the span. 635 func newUserArenaChunk() (unsafe.Pointer, *mspan) { 636 if gcphase == _GCmarktermination { 637 throw("newUserArenaChunk called with gcphase == _GCmarktermination") 638 } 639 640 // Deduct assist credit. Because user arena chunks are modeled as one 641 // giant heap object which counts toward heapLive, we're obligated to 642 // assist the GC proportionally (and it's worth noting that the arena 643 // does represent additional work for the GC, but we also have no idea 644 // what that looks like until we actually allocate things into the 645 // arena). 646 deductAssistCredit(userArenaChunkBytes) 647 648 // Set mp.mallocing to keep from being preempted by GC. 649 mp := acquirem() 650 if mp.mallocing != 0 { 651 throw("malloc deadlock") 652 } 653 if mp.gsignal == getg() { 654 throw("malloc during signal") 655 } 656 mp.mallocing = 1 657 658 // Allocate a new user arena. 659 var span *mspan 660 systemstack(func() { 661 span = mheap_.allocUserArenaChunk() 662 }) 663 if span == nil { 664 throw("out of memory") 665 } 666 x := unsafe.Pointer(span.base()) 667 668 // Allocate black during GC. 669 // All slots hold nil so no scanning is needed. 670 // This may be racing with GC so do it atomically if there can be 671 // a race marking the bit. 672 if gcphase != _GCoff { 673 gcmarknewobject(span, span.base(), span.elemsize) 674 } 675 676 if raceenabled { 677 // TODO(mknyszek): Track individual objects. 678 racemalloc(unsafe.Pointer(span.base()), span.elemsize) 679 } 680 681 if msanenabled { 682 // TODO(mknyszek): Track individual objects. 683 msanmalloc(unsafe.Pointer(span.base()), span.elemsize) 684 } 685 686 if asanenabled { 687 // TODO(mknyszek): Track individual objects. 688 rzSize := computeRZlog(span.elemsize) 689 span.elemsize -= rzSize 690 span.limit -= rzSize 691 span.userArenaChunkFree = makeAddrRange(span.base(), span.limit) 692 asanpoison(unsafe.Pointer(span.limit), span.npages*pageSize-span.elemsize) 693 asanunpoison(unsafe.Pointer(span.base()), span.elemsize) 694 } 695 696 if rate := MemProfileRate; rate > 0 { 697 c := getMCache(mp) 698 if c == nil { 699 throw("newUserArenaChunk called without a P or outside bootstrapping") 700 } 701 // Note cache c only valid while m acquired; see #47302 702 if rate != 1 && userArenaChunkBytes < c.nextSample { 703 c.nextSample -= userArenaChunkBytes 704 } else { 705 profilealloc(mp, unsafe.Pointer(span.base()), userArenaChunkBytes) 706 } 707 } 708 mp.mallocing = 0 709 releasem(mp) 710 711 // Again, because this chunk counts toward heapLive, potentially trigger a GC. 712 if t := (gcTrigger{kind: gcTriggerHeap}); t.test() { 713 gcStart(t) 714 } 715 716 if debug.malloc { 717 if debug.allocfreetrace != 0 { 718 tracealloc(unsafe.Pointer(span.base()), userArenaChunkBytes, nil) 719 } 720 721 if inittrace.active && inittrace.id == getg().goid { 722 // Init functions are executed sequentially in a single goroutine. 723 inittrace.bytes += uint64(userArenaChunkBytes) 724 } 725 } 726 727 // Double-check it's aligned to the physical page size. Based on the current 728 // implementation this is trivially true, but it need not be in the future. 729 // However, if it's not aligned to the physical page size then we can't properly 730 // set it to fault later. 731 if uintptr(x)%physPageSize != 0 { 732 throw("user arena chunk is not aligned to the physical page size") 733 } 734 735 return x, span 736 } 737 738 // isUnusedUserArenaChunk indicates that the arena chunk has been set to fault 739 // and doesn't contain any scannable memory anymore. However, it might still be 740 // mSpanInUse as it sits on the quarantine list, since it needs to be swept. 741 // 742 // This is not safe to execute unless the caller has ownership of the mspan or 743 // the world is stopped (preemption is prevented while the relevant state changes). 744 // 745 // This is really only meant to be used by accounting tests in the runtime to 746 // distinguish when a span shouldn't be counted (since mSpanInUse might not be 747 // enough). 748 func (s *mspan) isUnusedUserArenaChunk() bool { 749 return s.isUserArenaChunk && s.spanclass == makeSpanClass(0, true) 750 } 751 752 // setUserArenaChunkToFault sets the address space for the user arena chunk to fault 753 // and releases any underlying memory resources. 754 // 755 // Must be in a non-preemptible state to ensure the consistency of statistics 756 // exported to MemStats. 757 func (s *mspan) setUserArenaChunkToFault() { 758 if !s.isUserArenaChunk { 759 throw("invalid span in heapArena for user arena") 760 } 761 if s.npages*pageSize != userArenaChunkBytes { 762 throw("span on userArena.faultList has invalid size") 763 } 764 765 // Update the span class to be noscan. What we want to happen is that 766 // any pointer into the span keeps it from getting recycled, so we want 767 // the mark bit to get set, but we're about to set the address space to fault, 768 // so we have to prevent the GC from scanning this memory. 769 // 770 // It's OK to set it here because (1) a GC isn't in progress, so the scanning code 771 // won't make a bad decision, (2) we're currently non-preemptible and in the runtime, 772 // so a GC is blocked from starting. We might race with sweeping, which could 773 // put it on the "wrong" sweep list, but really don't care because the chunk is 774 // treated as a large object span and there's no meaningful difference between scan 775 // and noscan large objects in the sweeper. The STW at the start of the GC acts as a 776 // barrier for this update. 777 s.spanclass = makeSpanClass(0, true) 778 779 // Actually set the arena chunk to fault, so we'll get dangling pointer errors. 780 // sysFault currently uses a method on each OS that forces it to evacuate all 781 // memory backing the chunk. 782 sysFault(unsafe.Pointer(s.base()), s.npages*pageSize) 783 784 // Everything on the list is counted as in-use, however sysFault transitions to 785 // Reserved, not Prepared, so we skip updating heapFree or heapReleased and just 786 // remove the memory from the total altogether; it's just address space now. 787 gcController.heapInUse.add(-int64(s.npages * pageSize)) 788 789 // Count this as a free of an object right now as opposed to when 790 // the span gets off the quarantine list. The main reason is so that the 791 // amount of bytes allocated doesn't exceed how much is counted as 792 // "mapped ready," which could cause a deadlock in the pacer. 793 gcController.totalFree.Add(int64(s.npages * pageSize)) 794 795 // Update consistent stats to match. 796 // 797 // We're non-preemptible, so it's safe to update consistent stats (our P 798 // won't change out from under us). 799 stats := memstats.heapStats.acquire() 800 atomic.Xaddint64(&stats.committed, -int64(s.npages*pageSize)) 801 atomic.Xaddint64(&stats.inHeap, -int64(s.npages*pageSize)) 802 atomic.Xadd64(&stats.largeFreeCount, 1) 803 atomic.Xadd64(&stats.largeFree, int64(s.npages*pageSize)) 804 memstats.heapStats.release() 805 806 // This counts as a free, so update heapLive. 807 gcController.update(-int64(s.npages*pageSize), 0) 808 809 // Mark it as free for the race detector. 810 if raceenabled { 811 racefree(unsafe.Pointer(s.base()), s.elemsize) 812 } 813 814 systemstack(func() { 815 // Add the user arena to the quarantine list. 816 lock(&mheap_.lock) 817 mheap_.userArena.quarantineList.insert(s) 818 unlock(&mheap_.lock) 819 }) 820 } 821 822 // inUserArenaChunk returns true if p points to a user arena chunk. 823 func inUserArenaChunk(p uintptr) bool { 824 s := spanOf(p) 825 if s == nil { 826 return false 827 } 828 return s.isUserArenaChunk 829 } 830 831 // freeUserArenaChunk releases the user arena represented by s back to the runtime. 832 // 833 // x must be a live pointer within s. 834 // 835 // The runtime will set the user arena to fault once it's safe (the GC is no longer running) 836 // and then once the user arena is no longer referenced by the application, will allow it to 837 // be reused. 838 func freeUserArenaChunk(s *mspan, x unsafe.Pointer) { 839 if !s.isUserArenaChunk { 840 throw("span is not for a user arena") 841 } 842 if s.npages*pageSize != userArenaChunkBytes { 843 throw("invalid user arena span size") 844 } 845 846 // Mark the region as free to various santizers immediately instead 847 // of handling them at sweep time. 848 if raceenabled { 849 racefree(unsafe.Pointer(s.base()), s.elemsize) 850 } 851 if msanenabled { 852 msanfree(unsafe.Pointer(s.base()), s.elemsize) 853 } 854 if asanenabled { 855 asanpoison(unsafe.Pointer(s.base()), s.elemsize) 856 } 857 858 // Make ourselves non-preemptible as we manipulate state and statistics. 859 // 860 // Also required by setUserArenaChunksToFault. 861 mp := acquirem() 862 863 // We can only set user arenas to fault if we're in the _GCoff phase. 864 if gcphase == _GCoff { 865 lock(&userArenaState.lock) 866 faultList := userArenaState.fault 867 userArenaState.fault = nil 868 unlock(&userArenaState.lock) 869 870 s.setUserArenaChunkToFault() 871 for _, lc := range faultList { 872 lc.mspan.setUserArenaChunkToFault() 873 } 874 875 // Until the chunks are set to fault, keep them alive via the fault list. 876 KeepAlive(x) 877 KeepAlive(faultList) 878 } else { 879 // Put the user arena on the fault list. 880 lock(&userArenaState.lock) 881 userArenaState.fault = append(userArenaState.fault, liveUserArenaChunk{s, x}) 882 unlock(&userArenaState.lock) 883 } 884 releasem(mp) 885 } 886 887 // allocUserArenaChunk attempts to reuse a free user arena chunk represented 888 // as a span. 889 // 890 // Must be in a non-preemptible state to ensure the consistency of statistics 891 // exported to MemStats. 892 // 893 // Acquires the heap lock. Must run on the system stack for that reason. 894 // 895 //go:systemstack 896 func (h *mheap) allocUserArenaChunk() *mspan { 897 var s *mspan 898 var base uintptr 899 900 // First check the free list. 901 lock(&h.lock) 902 if !h.userArena.readyList.isEmpty() { 903 s = h.userArena.readyList.first 904 h.userArena.readyList.remove(s) 905 base = s.base() 906 } else { 907 // Free list was empty, so allocate a new arena. 908 hintList := &h.userArena.arenaHints 909 if raceenabled { 910 // In race mode just use the regular heap hints. We might fragment 911 // the address space, but the race detector requires that the heap 912 // is mapped contiguously. 913 hintList = &h.arenaHints 914 } 915 v, size := h.sysAlloc(userArenaChunkBytes, hintList, false) 916 if size%userArenaChunkBytes != 0 { 917 throw("sysAlloc size is not divisible by userArenaChunkBytes") 918 } 919 if size > userArenaChunkBytes { 920 // We got more than we asked for. This can happen if 921 // heapArenaSize > userArenaChunkSize, or if sysAlloc just returns 922 // some extra as a result of trying to find an aligned region. 923 // 924 // Divide it up and put it on the ready list. 925 for i := uintptr(userArenaChunkBytes); i < size; i += userArenaChunkBytes { 926 s := h.allocMSpanLocked() 927 s.init(uintptr(v)+i, userArenaChunkPages) 928 h.userArena.readyList.insertBack(s) 929 } 930 size = userArenaChunkBytes 931 } 932 base = uintptr(v) 933 if base == 0 { 934 // Out of memory. 935 unlock(&h.lock) 936 return nil 937 } 938 s = h.allocMSpanLocked() 939 } 940 unlock(&h.lock) 941 942 // sysAlloc returns Reserved address space, and any span we're 943 // reusing is set to fault (so, also Reserved), so transition 944 // it to Prepared and then Ready. 945 // 946 // Unlike (*mheap).grow, just map in everything that we 947 // asked for. We're likely going to use it all. 948 sysMap(unsafe.Pointer(base), userArenaChunkBytes, &gcController.heapReleased) 949 sysUsed(unsafe.Pointer(base), userArenaChunkBytes, userArenaChunkBytes) 950 951 // Model the user arena as a heap span for a large object. 952 spc := makeSpanClass(0, false) 953 h.initSpan(s, spanAllocHeap, spc, base, userArenaChunkPages) 954 s.isUserArenaChunk = true 955 956 // Account for this new arena chunk memory. 957 gcController.heapInUse.add(int64(userArenaChunkBytes)) 958 gcController.heapReleased.add(-int64(userArenaChunkBytes)) 959 960 stats := memstats.heapStats.acquire() 961 atomic.Xaddint64(&stats.inHeap, int64(userArenaChunkBytes)) 962 atomic.Xaddint64(&stats.committed, int64(userArenaChunkBytes)) 963 964 // Model the arena as a single large malloc. 965 atomic.Xadd64(&stats.largeAlloc, int64(userArenaChunkBytes)) 966 atomic.Xadd64(&stats.largeAllocCount, 1) 967 memstats.heapStats.release() 968 969 // Count the alloc in inconsistent, internal stats. 970 gcController.totalAlloc.Add(int64(userArenaChunkBytes)) 971 972 // Update heapLive. 973 gcController.update(int64(userArenaChunkBytes), 0) 974 975 // Put the large span in the mcentral swept list so that it's 976 // visible to the background sweeper. 977 h.central[spc].mcentral.fullSwept(h.sweepgen).push(s) 978 s.limit = s.base() + userArenaChunkBytes 979 s.freeindex = 1 980 s.allocCount = 1 981 982 // This must clear the entire heap bitmap so that it's safe 983 // to allocate noscan data without writing anything out. 984 s.initHeapBits(true) 985 986 // Clear the span preemptively. It's an arena chunk, so let's assume 987 // everything is going to be used. 988 // 989 // This also seems to make a massive difference as to whether or 990 // not Linux decides to back this memory with transparent huge 991 // pages. There's latency involved in this zeroing, but the hugepage 992 // gains are almost always worth it. Note: it's important that we 993 // clear even if it's freshly mapped and we know there's no point 994 // to zeroing as *that* is the critical signal to use huge pages. 995 memclrNoHeapPointers(unsafe.Pointer(s.base()), s.elemsize) 996 s.needzero = 0 997 998 s.freeIndexForScan = 1 999 1000 // Set up the range for allocation. 1001 s.userArenaChunkFree = makeAddrRange(base, s.limit) 1002 return s 1003 }