memstats 结构中的哪些字段仅指堆,仅指堆栈

Go 运行时有很多与堆和堆栈相关的不同变量,并且一些堆栈编号是堆编号的一部分,导致混淆(对我而言)。例如,在此链接中。它说


// Stack numbers are part of the heap numbers, separate those out for user consumption

    stats.StackSys = stats.StackInuse

    stats.HeapInuse -= stats.StackInuse

    stats.HeapSys -= stats.StackInuse

在运行时文档中(摘录如下),它给出了 7 个不同的与堆相关的字段(即 memstat 结构的字段),但没有明确说明哪些包含堆栈,类似地,堆中包含哪些堆栈字段,以及它们与总拨款。


这是一个问题,因为我想将堆与堆栈进行比较,但我不想选择包含堆栈的堆变量(显然)。


问题 1). 总分配字段是否包括堆、堆栈或两者?2) 哪些堆字段不包括数字栈?3) 哪些堆字段包含栈的数字?4) 哪些堆栈字段不包含堆的数字?


  Alloc      uint64 // bytes allocated and still in use

        TotalAlloc uint64 // bytes allocated (even if freed)

        Sys        uint64 // bytes obtained from system (sum of XxxSys below)

        Lookups    uint64 // number of pointer lookups

        Mallocs    uint64 // number of mallocs

        Frees      uint64 // number of frees


        // Main allocation heap statistics.

        HeapAlloc    uint64 // bytes allocated and still in use

        HeapSys      uint64 // bytes obtained from system

        HeapIdle     uint64 // bytes in idle spans

        HeapInuse    uint64 // bytes in non-idle span

        HeapReleased uint64 // bytes released to the OS

        HeapObjects  uint64 // total number of allocated objects


        // Low-level fixed-size structure allocator statistics.

        //  Inuse is bytes used now.

        //  Sys is bytes obtained from system.

        StackInuse  uint64 // bytes used by stack allocator

        StackSys    uint64


炎炎设计
浏览 222回答 2
2回答

红糖糍粑

这些问题有点难以回答,因为 goroutine 堆栈是从堆中分配的。Go 没有 C 中存在的堆栈和堆之间的明确分离。总分配字段是否包括堆、堆栈或两者?MemStats 结构的 TotalAlloc 字段包括 Go 运行时为 Go 堆从 OS 请求的所有内存。它不包括为 goroutine 堆栈分配的内存。一开始我以为是这样,但我错了。对困惑感到抱歉。我希望这个答案更准确。(准确地说,我应该提到,在使用 cgo 的程序中,每个线程(不是 goroutine——goroutine 通常比线程多)都有一个由操作系统分配的堆栈;该堆栈不是由 Go 运行时分配的,而是不计入 TotalAlloc。它只被 cgo 调用使用。)哪些堆字段不包括数字堆栈? 哪些堆字段包括堆栈的数字?这些字段包括 goroutine 堆栈的编号:HeapIdle、HeapReleased。这些字段不包括 goroutine 堆栈的数字:HeapAlloc、HeapInUse、HeapObjects。HeapSys 字段不包括当前活动的 goroutine 堆栈使用的内存,但包括曾经使用但随后被释放的 goroutine 堆栈的内存。哪些堆栈字段不包含堆的数字?我不知道如何以有意义的方式回答这个问题。堆栈字段专门报告有关 goroutine 堆栈的信息。

交互式爱情

通过运行(变体)测试程序并查看 Go 源代码,我看到:Alloc 和 TotalAlloc 似乎只涵盖非堆栈分配。分配大局部变量并没有将它们的大小增加到 TotalAlloc,即使它导致堆栈增长。如果内存当前是为 goroutine 的堆栈保留的,它会计入 StackX 变量而不是 HeapX 变量。这是您在源代码中找到的减法。它还意味着为堆栈分配空间的任何事情都可以减少 HeapSys 和 HeapIdle 但不影响 HeapInuse。因为你问:堆栈字段从不包括堆分配——堆栈来自堆,反之亦然。ssp := new(SomeStruct)如果逃逸分析可以确定它们不会超过函数调用,那么您认为在堆 (&nbsp;)上的变量实际上可能是堆栈分配的。这几乎总是对您有用,因为可以在函数退出时释放这些变量,而不会为 GC 生成垃圾。不要太担心这个。:)一旦 goroutine 退出,它的堆栈空间可以返回到堆中。(不过,如果它的堆栈很小,它可能会被缓存以作为未来 goroutine 的堆栈重用。)然后它不会显示为堆栈空间,并可能再次显示为可用的堆空间。我在经验和 Go 源代码中都看到了这一点(proc.c 的 gfput 调用 runtime·stackfree)。这意味着退出 goroutines 或在堆栈增长后返回的旧堆栈似乎会增长 HeapSys 和 HeapIdle,但这实际上只是使用之间的空间转移。似乎没有 TotalAlloc 风格的运行计数器覆盖为堆栈分配的所有页面。如果一个 goroutine 的堆栈被释放和重用,它只会被计数一次。绝对没有 TotalAlloc 风格的运行计数器涵盖所有堆栈分配的变量。这将涉及跟踪每个函数调用的开销。堆栈相关的问题相对较少,因为堆栈分配的变量在函数返回时被释放,而大堆栈本身在 goroutine 退出时被释放。它们可能会发生,比如如果 goroutine 泄漏(即使在创建新的 goroutine 时也不会退出),或者如果您var bigLocal [1e7]uint64在不退出的 goroutine 中进行了巨大的堆栈分配(或荒谬的深度递归)。但是更常见的是堆出现问题,因为堆上的东西直到 GC 才会被释放(像标准这样的工具可以Pool帮助您回收堆分配的项目以延迟对 GC 的需求)。因此,实际上,我主要会关注 Alloc 和 TotalAlloc 是否存在堆过度使用的情况,如果不知何故堆栈空间成为问题,请查看堆栈编号(并且可能会检查是否有许多意外运行的 goroutines)。这些观察是特定于实现的(我在看 go 1.4,而不是提示),而且我不是 Go 源代码的专家,所以就看它们是什么。那个测试程序,供参考:package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "reflect"&nbsp; &nbsp; "runtime"&nbsp; &nbsp; "sync")var g []bytefunc usesHeap() {&nbsp; &nbsp; g = make([]byte, 1000)}func usesTempStack() {&nbsp; &nbsp; var l [1000]byte&nbsp; &nbsp; _ = l}func createsGoroutineAndWaits() {&nbsp; &nbsp; wg := new(sync.WaitGroup)&nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; usesTempStack()&nbsp; &nbsp; &nbsp; &nbsp; wg.Done()&nbsp; &nbsp; }()&nbsp; &nbsp; wg.Wait()}func createsGoroutine() {&nbsp; &nbsp; go usesTempStack()}func recurse(depth int, max int) {&nbsp; &nbsp; var l [1024]byte&nbsp; &nbsp; _ = l&nbsp; &nbsp; if depth < max {&nbsp; &nbsp; &nbsp; &nbsp; recurse(depth+1, max)&nbsp; &nbsp; }}func growsTheStack() {&nbsp; &nbsp; recurse(0, 1000)}func checkUsageOf(lbl string, f func(), before, after *runtime.MemStats) {&nbsp; &nbsp; _ = new(sync.WaitGroup)&nbsp; &nbsp; runtime.ReadMemStats(before)&nbsp; &nbsp; // using own goroutine so everyone starts w/the same stack size&nbsp; &nbsp; wg := new(sync.WaitGroup)&nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; // request GC in hopes of a fair start&nbsp; &nbsp; runtime.GC()&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; runtime.ReadMemStats(before)&nbsp; &nbsp; &nbsp; &nbsp; for i := 0; i < 1000; i++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f()&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; runtime.Gosched()&nbsp; &nbsp; &nbsp; &nbsp; runtime.ReadMemStats(after)&nbsp; &nbsp; &nbsp; &nbsp; wg.Done()&nbsp; &nbsp; }()&nbsp; &nbsp; wg.Wait()&nbsp; &nbsp; fmt.Println("Results for", lbl, "\n")&nbsp; &nbsp; beforeVal, afterVal := reflect.ValueOf(*before), reflect.ValueOf(*after)&nbsp; &nbsp; memStatsType := beforeVal.Type()&nbsp; &nbsp; fieldCount := memStatsType.NumField()&nbsp; &nbsp; for i := 0; i < fieldCount; i++ {&nbsp; &nbsp; &nbsp; &nbsp; field := memStatsType.Field(i)&nbsp; &nbsp; &nbsp; &nbsp; if field.Type.Kind() != reflect.Uint64 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; beforeStat, afterStat := int64(beforeVal.Field(i).Uint()), int64(afterVal.Field(i).Uint())&nbsp; &nbsp; &nbsp; &nbsp; if beforeStat == afterStat {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(field.Name, "differs by", afterStat-beforeStat)&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println("\n")}func main() {&nbsp; &nbsp; before, after := new(runtime.MemStats), new(runtime.MemStats)&nbsp; &nbsp; checkUsageOf("growsTheStack", growsTheStack, before, after)&nbsp; &nbsp; checkUsageOf("createsGoroutineAndWaits", createsGoroutine, before, after)&nbsp; &nbsp; checkUsageOf("usesHeap", usesHeap, before, after)&nbsp; &nbsp; checkUsageOf("usesTempStack", usesTempStack, before, after)&nbsp; &nbsp; checkUsageOf("createsGoroutine", createsGoroutine, before, after)}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go