上卷 程序设计
中卷 标准库
- 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.2.2 堆内存管理
堆内存管理包括:
- 向操作系统申请内存。
- 为上层部件分配内存。(alloc, mspan)
- 管理正在使用和闲置内存。
- 回收不再使用的内存。(free)
分配、重用和回收管理,由独立单元 pageAlloc 完成。
// mheap.go type mheap struct { pages pageAlloc // page allocation data structure } // Initialize the heap. func (h *mheap) init() { h.pages.init(&h.lock, &memstats.gcMiscSys) }
相关元数据
- summary: 用于查找可用内存块的摘要。
- chunks: 标记使用和释放状态的位图。
// mpagealloc.go // Page allocator. // // The page allocator manages mapped pages (defined by pageSize, NOT // physPageSize) for allocation and re-use. It is embedded into mheap. // // Pages are managed using a bitmap that is sharded into chunks. // In the bitmap, 1 means in-use, and 0 means free. // // Each entry in the radix tree is a summary that describes three properties of // a particular region of the address space: the number of contiguous free pages // at the start and end of the region it represents, and the maximum number of // contiguous free pages found anywhere in that region. type pageAlloc struct { // Radix tree of summaries. summary [summaryLevels][]pallocSum // chunks is a slice of bitmap chunks. chunks [1 << pallocChunksL1Bits]*[1 << pallocChunksL2Bits]pallocData }
所谓摘要(summary),就是将内存块(chunk)的 start、end、max 打包成一个整数。
// mpagealloc.go // pallocSum is a packed summary type which packs three numbers: start, max, // and end into a single 8-byte value. type pallocSum uint64 func packPallocSum(start, max, end uint) pallocSum { return pallocSum((uint64(start) & (maxPackedValue - 1)) | ((uint64(max) & (maxPackedValue - 1)) << logMaxPackedValue) | ((uint64(end) & (maxPackedValue - 1)) << (2 * logMaxPackedValue))) }
使用位图(pallocBits),意味着堆内部不再用 mspan 管理。
需要时,查找摘要,返回可用地址,标记相关位图即可。
// mpallocbits.go // pageBits is a bitmap representing one bit per page in a palloc chunk. type pageBits [pallocChunkPages / 64]uint64 // pallocBits is a bitmap that tracks page allocations for at most one // palloc chunk. type pallocBits pageBits type pallocData struct { pallocBits // 内存分配位图。 scavenged pageBits // 物理释放位图。 }
分配
分配操作获取内存,返回 mspan 对象,并初始化相关属性。
// mheap.go // alloc allocates a new span of npage pages from the GC'd heap. func (h *mheap) alloc(npages uintptr, spanclass spanClass) *mspan { s = h.allocSpan(npages, spanAllocHeap, spanclass) return s }
// allocSpan allocates an mspan which owns npages worth of memory. func (h *mheap) allocSpan(npages uintptr, typ spanAllocType, spanclass spanClass) (s *mspan) { base, scav := uintptr(0), uintptr(0) if base == 0 { // 分配。 base, scav = h.pages.alloc(npages) // 失败!扩张后重试。 if base == 0 { var ok bool growth, ok = h.grow(npages) if !ok { return nil } base, scav = h.pages.alloc(npages) } } // 创建 mspan 管理对象。 if s == nil { s = h.allocMSpanLocked() } // 发生内存扩张,积极释放闲置物理内存。 if growth > 0 { // We just caused a heap growth, so scavenge down what will soon be used. h.pages.scavenge(todo) } HaveSpan: // 初始化 mspan。 s.init(base, npages) nbytes := npages * pageSize // 设置属性。 s.spanclass = spanclass ... s.gcmarkBits = newMarkBits(s.nelems) s.allocBits = newAllocBits(s.nelems) atomic.Store(&s.sweepgen, h.sweepgen) s.state.set(mSpanInUse) // 补上被释放的部分(相邻多块被合并后可能出现的状态)。 if scav != 0 { // sysUsed all the pages that are actually available // in the span since some of them might be scavenged. sysUsed(unsafe.Pointer(base), nbytes) } // 填充反查表。 h.setSpans(s.base(), npages, s) return s }
核心是 pageAlloc.alloc 操作。
- 基于摘要查找可用内存块。
- 更新位图信息。
// mpagealloc.go // alloc allocates npages worth of memory from the page heap, returning the base // address for the allocation and the amount of scavenged memory in bytes // contained in the region [base address, base address + npages*pageSize). func (p *pageAlloc) alloc(npages uintptr) (addr uintptr, scav uintptr) { // 直接搜索。 searchAddr := minOffAddr if pallocChunkPages-chunkPageIndex(p.searchAddr.addr()) >= uint(npages) { i := chunkIndex(p.searchAddr.addr()) if max := p.summary[len(p.summary)-1][i].max(); max >= uint(npages) { j, searchIdx := p.chunkOf(i).find(npages, chunkPageIndex(p.searchAddr.addr())) addr = chunkBase(i) + uintptr(j)*pageSize goto Found } } // 慢搜索。 addr, searchAddr = p.find(npages) if addr == 0 { return 0, 0 } Found: // 标记使用和释放位图。 scav = p.allocRange(addr, npages) return addr, scav }
// allocRange marks the range of memory [base, base+npages*pageSize) as // allocated. It also updates the summaries to reflect the newly-updated // bitmap. func (p *pageAlloc) allocRange(base, npages uintptr) uintptr { chunk = p.chunkOf(ec) scav += chunk.scavenged.popcntRange(0, ei+1) // 更新位图。 chunk.allocRange(0, ei+1) // 更新摘要。 p.update(base, npages, true, true) return uintptr(scav) * pageSize }
// mpallocbits.go // allocRange sets bits [i, i+n) in the bitmap to 1 and // updates the scavenged bits appropriately. func (m *pallocData) allocRange(i, n uint) { m.pallocBits.allocRange(i, n) m.scavenged.clearRange(i, n) }
如没找到适用闲置块,则向操作系统申请。
// mpagealloc.go func (p *pageAlloc) grow(base, size uintptr) { limit := alignUp(base+size, pallocChunkBytes) base = alignDown(base, pallocChunkBytes) // 申请新内存。 p.sysGrow(base, limit) // 更新位图。 for c := chunkIndex(base); c < chunkIndex(limit); c++ { if p.chunks[c.l1()] == nil { r := sysAlloc(unsafe.Sizeof(*p.chunks[0]), p.sysStat) atomic.StorepNoWB(unsafe.Pointer(&p.chunks[c.l1()]), r) } p.chunkOf(c).scavenged.setRange(0, pallocChunkPages) } // 更新摘要。 p.update(base, size/pageSize, true, false) }
回收
向堆归还内存块。
// mheap.go // Free the span back into the heap. func (h *mheap) freeSpan(s *mspan) { h.freeSpanLocked(s, spanAllocHeap) }
func (h *mheap) freeSpanLocked(s *mspan, typ spanAllocType) { // 标记内存被闲置。 h.pages.free(s.base(), s.npages, false) // 释放 mspan 管理对象。 s.state.set(mSpanDead) h.freeMSpanLocked(s) }
所有内存都以元数据进行管理,所以释放也只需清除位图标记,同时更新摘要即可。
// mpagealloc.go // free returns npages worth of memory starting at base back to the page heap. func (p *pageAlloc) free(base, npages uintptr, scavenged bool) { // 更新位图。 if npages == 1 { // large p.chunkOf(i).free1(chunkPageIndex(base)) } else { p.chunkOf(sc).free(si, ei+1-si) } // 更新摘要。 p.update(base, npages, true, false) }
// mpallocbits.go // free frees the range [i, i+n) of pages in the pallocBits. func (b *pallocBits) free(i, n uint) { (*pageBits)(b).clearRange(i, n) }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论