Go 运行时对用户提供只读的内存统计信息,通过 runtime.MemStats 支持。 公共方法只有一个:ReadMemStats。但调用这个方法的代价非常之大:
    func ReadMemStats(m *MemStats) {
    stopTheWorld(“read mem stats”)

    systemstack(func() {
    readmemstats_m(m)
    })

    startTheWorld()
    }
    读取前后需要付出 STW 的成本。对于 readmemstats_m 而言,是将运行时用于内存统计的 变量 memstats 中的值拷贝到用户态的 MemStats 中,不过这样进行 memove 操作 是发生在系统栈上的,因此这部分的内存实际上是 OS 栈上的内存,因此最后还给用户加上 stats.StackInuse 的值来保证完整性:
    func readmemstats_m(stats *MemStats) {
    updatememstats()

    // 将运行时 memstats 变量拷贝到 stats 中
    memmove(unsafe.Pointer(stats), unsafe.Pointer(&memstats), sizeof_C_MStats)

    // 因为 memstats.stackssys 是唯一直接映射到 OS 栈的内存。
    // 所以这里加上了堆分配的栈内存以供用户使用。
    stats.StackSys += stats.StackInuse
    }
    而上方拷贝前的 updatememstats 是为了将 STW 之后未完整统计的内存信息统一更新到 memstats 中:
    //go:nowritebarrier
    func updatememstats() {
    memstats.mcache_inuse = uint64(mheap
    .cachealloc.inuse)
    memstats.mspaninuse = uint64(mheap.spanalloc.inuse)
    memstats.sys = memstats.heap_sys + memstats.stacks_sys + memstats.mspan_sys +
    memstats.mcache_sys + memstats.buckhash_sys + memstats.gc_sys + memstats.other_sys

    // 将 stacks_inuse 作为系统内存进行计算
    memstats.sys += memstats.stacks_inuse

    // 计算内存分配器统计信息。
    // 在程序执行期间,运行时只计算释放的数量和释放的内存量。
    // 堆中当前活动对象的数量和活动堆内存的数量
    // 通过扫描所有 span 计算。
    // malloc 的总数计算为 frees 数和活动对象数。
    // 类似地,分配的内存总量计算为释放的内存量
    // 加上活跃堆内存的数量。
    memstats.alloc = 0
    memstats.total_alloc = 0
    memstats.nmalloc = 0
    memstats.nfree = 0
    for i := 0; i < len(memstats.by_size); i++ {
    memstats.by_size[i].nmalloc = 0
    memstats.by_size[i].nfree = 0
    }

    // Flush mcaches 到 mcentral, TODO: 这个地方不是很明白为什么还要切一次系统栈?
    systemstack(flushallmcaches)

    // 汇总本地统计数据。
    cachestats()

    // 统计分配信息,因为 STW 所以安全
    var smallFree, totalAlloc, totalFree uint64
    // 搜集每个 span 等级的统计
    for spc := range mheap.central {
    // mcaches 现在为空,因此 mcentral 统计已经是最新的了
    c := &mheap
    .central[spc].mcentral
    memstats.nmalloc += c.nmalloc
    i := spanClass(spc).sizeclass()
    memstats.bysize[i].nmalloc += c.nmalloc
    totalAlloc += c.nmalloc * uint64(class_to_size[i])
    }
    // 收集每个大小等级的信息
    for i := 0; i < _NumSizeClasses; i++ {
    if i == 0 {
    memstats.nmalloc += mheap
    .nlargealloc
    totalAlloc += mheap.largealloc
    totalFree += mheap
    .largefree
    memstats.nfree += mheap_.nlargefree
    continue
    }

    1. // The mcache stats have been flushed to mheap_.<br /> memstats.nfree += mheap_.nsmallfree[i]<br /> memstats.by_size[i].nfree = mheap_.nsmallfree[i]<br /> smallFree += mheap_.nsmallfree[i] * uint64(class_to_size[i])<br /> }<br />totalFree += smallFree

    memstats.nfree += memstats.tinyallocs
    memstats.nmalloc += memstats.tinyallocs

    // 计算派生数据
    memstats.totalalloc = totalAlloc
    memstats.alloc = totalAlloc - totalFree
    memstats.heap_alloc = memstats.alloc
    memstats.heap_objects = memstats.nmalloc - memstats.nfree
    }
    //go:nowritebarrier
    func flushallmcaches() {
    for i := 0; i < int(gomaxprocs); i++ {
    flushmcache(i)
    }
    }
    //go:nowritebarrier
    func flushmcache(i int) {
    p := allp[i]
    c := p.mcache
    if c == nil {
    return
    }
    c.releaseAll()
    stackcache_clear(c)
    }
    //go:systemstack
    func stackcache_clear(c *mcache) {
    (…)
    lock(&stackpoolmu)
    for order := uint8(0); order < _NumStackOrders; order++ {
    x := c.stackcache[order].list
    for x.ptr() != nil {
    y := x.ptr().next
    stackpoolfree(x, order)
    x = y
    }
    c.stackcache[order].list = 0
    c.stackcache[order].size = 0
    }
    unlock(&stackpoolmu)
    }
    //go:nowritebarrier
    func cachestats() {
    for
    , p := range allp {
    c := p.mcache
    if c == nil {
    continue
    }
    purgecachedstats(c)
    }
    }
    //go:nosplit
    func purgecachedstats(c *mcache) {
    // Protected by either heap or GC lock.
    h := &mheap_
    memstats.heap_scan += uint64(c.local_scan)
    c.local_scan = 0
    memstats.tinyallocs += uint64(c.local_tinyallocs)
    c.local_tinyallocs = 0
    h.largefree += uint64(c.local_largefree)
    c.local_largefree = 0
    h.nlargefree += uint64(c.local_nlargefree)
    c.local_nlargefree = 0
    for i := 0; i < len(c.local_nsmallfree); i++ {
    h.nsmallfree[i] += uint64(c.local_nsmallfree[i])
    c.local_nsmallfree[i] = 0
    }
    }
    这里只是读取时候对全部信息进行的同步,在分配的过程中还有很多直接统计的代码。