上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
2.6.1 固定分配器
前文所提内存,多指用户对象存储。
但编译后的程序,实际由用户和运行时两部分代码共同组成。
运行时相关操作,同样需要分配内存。只是这些系统对象不在保留区域分配,且采取不同生命周期和复用策略。
为此,专门设计了 FixAlloc 固定分配器。
从堆数据结构中,可以看到几个使用固定分配器的字段。
// mheap.go type mheap struct { spanalloc fixalloc // allocator for span* cachealloc fixalloc // allocator for mcache* specialfinalizeralloc fixalloc // allocator for specialfinalizer* specialprofilealloc fixalloc // allocator for specialprofile* arenaHintAlloc fixalloc // allocator for arenaHints }
这其中,我们最为熟悉的是管理内存块的 mspan。
不同于 mheap 唯一实例,这几个字段各自持有独立固定分配器。
在堆初始化方法里,分别被设定不同的分配属性。
span 指代内存块,而 mspan 则是管理对象自身。
func (h *mheap) init() { h.spanalloc.init(unsafe.Sizeof(mspan{}), recordspan, ...) h.cachealloc.init(unsafe.Sizeof(mcache{}), ...) h.specialfinalizeralloc.init(unsafe.Sizeof(specialfinalizer{}), ...) h.specialprofilealloc.init(unsafe.Sizeof(specialprofile{}), ...) h.arenaHintAlloc.init(unsafe.Sizeof(arenaHint{}), ...) }
初始化
初始化时,除内存单元大小外,还可指定一关联函数和执行参数。
// mfixalloc.go // FixAlloc is a simple free-list allocator for fixed size objects. // Malloc uses a FixAlloc wrapped around sysAlloc to manage its // MCache and MSpan objects. type fixalloc struct { size uintptr first func(arg, p unsafe.Pointer) // called first time p is returned arg unsafe.Pointer }
func (f *fixalloc) init(size uintptr, first func(arg, p unsafe.Pointer), arg unsafe.Pointer, stat *uint64) { f.size = size f.first = first f.arg = arg f.list = nil f.chunk = 0 f.nchunk = 0 f.inuse = 0 f.stat = stat f.zero = true }
分配
从结构上看,固定分配器采用早期内存分配器设计思路。
以链表(list)管理回收可复用内存单元,另持一块待分割内存块(chunk)作为后备资源。
// mfixalloc.go type fixalloc struct { list *mlink chunk uintptr nchunk uint32 }
分配时优先从复用链表提取,不足再从后备内存分割。
如此看来,后备内存类似堆在三级结构中地位。
另外,关联函数仅在分割新内存单元时执行,很适合做些记录性工作。
func (f *fixalloc) alloc() unsafe.Pointer { // 尝试从复⽤链表提取。 if f.list != nil { v := unsafe.Pointer(f.list) f.list = f.list.next return v } // 如果后备资源不足,则重新获取 (16 KB)。 if uintptr(f.nchunk) < f.size { f.chunk = uintptr(persistentalloc(_FixAllocChunk, 0, f.stat)) f.nchunk = _FixAllocChunk } // 从后备内存块分割。 v := unsafe.Pointer(f.chunk) // 如有关联函数,则执⾏。 if f.first != nil { f.first(f.arg, v) } f.chunk = f.chunk + f.size f.nchunk -= uint32(f.size) return v }
func (h *mheap) allocMSpanLocked() *mspan { return (*mspan)(h.spanalloc.alloc()) }
回收
回收操作仅将内存单元放回复用链表,并没有与释放相关的行为。
使用 FixAlloc 的几种管理对象,其数量不会太多,不释放物理内存影响不大。
func (f *fixalloc) free(p unsafe.Pointer) { v := (*mlink)(p) v.next = f.list f.list = v }
手工调用,非 GC 回收。
// mheap.go func (h *mheap) freeSpanLocked(s *mspan, acctinuse, acctidle bool) { h.freeMSpanLocked(s) } func (h *mheap) freeMSpanLocked(s *mspan) { h.spanalloc.free(unsafe.Pointer(s)) }
后备
实际上,后备内存不仅仅为固定分配器服务,还频繁出现在运行时其他部件中。
// malloc.go type persistentAlloc struct { base *notInHeap off uintptr }
notInHeap 是空结构,包含一个计算偏移地址的方法。
type notInHeap struct{} func (p *notInHeap) add(bytes uintptr) *notInHeap { return (*notInHeap)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + bytes)) }
基于性能考虑,线程(P)拥有独立后备内存。另以全局变量作为补充。
// runtime2.go type p struct { palloc persistentAlloc }
// malloc.go var globalAlloc struct { mutex persistentAlloc }
优先从当前线程内无锁提取。如果不足,再向操作系统申请。
向操作系统申请内存时,直接使用 sysAlloc,避开内存分配器。
// malloc.go const persistentChunkSize = 256 << 10 func persistentalloc1(size, align uintptr, sysStat *uint64) *notInHeap { const ( maxBlock = 64 << 10 ) // 如果⼤大⼩小超过 64 KB,直接向操作系统申请。 if size >= maxBlock { return (*notInHeap)(sysAlloc(size, sysStat)) } var persistent *persistentAlloc // 确定后备内存位置 (本地或全局)。 if mp != nil && mp.p != 0 { persistent = &mp.p.ptr().palloc } else { persistent = &globalAlloc.persistentAlloc } // 如果内存不足,向操作系统申请 (256 KB)。 if persistent.off+size > persistentChunkSize || persistent.base == nil { persistent.base = (*notInHeap)(sysAlloc(persistentChunkSize, &memstats.other_sys)) persistent.off = alignUp(sys.PtrSize, align) } // 从后备内存切分。 p := persistent.base.add(persistent.off) persistent.off += size return p }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论