go - Go 如何以及何时为有界队列 channel 分配内存?

标签 go memory pprof

我正在使用 Go 的 pprof 工具来调查我的服务的内存使用情况。几乎所有的内存使用都来自一个设置多个有界队列 channel 的函数。我对 pprof 在这里告诉我的内容有些困惑:

$ go tool pprof ~/pprof/pprof.server.alloc_objects.alloc_space.inuse_objects.inuse_space.007.pb.gz
File: server
Type: inuse_space
Time: Dec 21, 2020 at 10:46am (PST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) list foo
Total: 102.73MB
ROUTINE ======================== github.com/******/foo in ***.go
   79.10MB    79.10MB (flat, cum) 77.00% of Total
         .          .    135:
         .          .    136:func foo() {
         .          .    137:    
   14.04MB    14.04MB    138:    chanA := make(chan chanAEntry, bufferSize)
         .          .    139:    defer close(chanA)
         .          .    140:
         .          .    141:    
   19.50MB    19.50MB    142:    chanB := make(chan chanBCEntry, bufferSize)
         .          .    143:    defer close(chanB)
         .          .    144:
         .          .    145:    
   27.53MB    27.53MB    146:    chanC := make(chan chanBCEntry, bufferSize)
         .          .    147:    defer close(chanC)
         .          .    148:
         .          .    149:    
    7.92MB     7.92MB    150:    chanD := make(chan chanDEntry, bufferSize)
         .          .    151:    defer close(chanD)
         .          .    152:
看起来第 142 行负责 19.50MB 的分配,第 146 行负责 27.53MB,但这些行做的是同样的事情——它们创建具有相同输入类型和相同容量的缓冲 channel 。
  • 这是 pprof 进行随机抽样这一事实的产物吗?
  • Go 是否会延迟分配 channel (fwiw,在让服务运行几天后,这些值最终会均衡)?
  • pprof 是否报告了沿 channel 发送的对象所需的内存以及 channel 本身所需的内存?
  • 最佳答案

    好的,我相信我已经想通了。看起来 Go 急切地分配并且差异只是由于 Go 内存分析器采样的方式。
    Go 急切分配 channel 内存
    docs对于 make promise

    The channel's buffer is initialized with the specified buffer capacity.


    我查看了 makechan 的代码,在 make(chan chantype, size) 期间被调用.它总是调用 mallocgc直接 - 没有懒惰。
    查看 mallocgc 的代码,我们可以确认mallocgc中没有惰性(除了没有提到懒惰的文档注释,mallocgc 直接调用 c.alloc)。
    pprof 在堆分配级别采样,而不是在调用函数级别
    环顾四周mallocgc ,我找到了分析代码。在每个 mallocgc调用,Go 会检查是否满足其采样条件。如果是,它会调用 mProf_Malloc将记录添加到堆配置文件。我无法确认这是 pprof 使用的配置文件,但该文件中的注释表明它是。
    采样条件基于自上次采样以来分配的字节数(它从指数分布中抽取样本,平均而言,在每 runtime.MemProfileRate 个字节分配后)。
    这里的重要部分是每次调用 mallocgc有一定概率被抽样,而不是每次调用 foo .这意味着如果调用 foo多次调用mallocgc , 我们预计 mallocgc 中只有一部分通话将被采样。
    把它们放在一起
    每次我的功能foo运行时,它会急切地为 4 个 channel 分配内存。在每次内存分配调用中,Go 都有机会记录堆配置文件。平均而言,Go 将每 512kB 记录一次堆配置文件(runtime.MemProfileRate 的默认值)。由于这些 channel 的总大小为 488kB,平均而言,我们预计每次 foo 只记录一个分配。叫做。我上面分享的配置文件是在服务重启后相对较快的,所以分配字节数的差异是纯统计差异的结果。让服务运行一天后,配置文件稳定下来,显示第 142 行和第 146 行分配的字节数相等。

    关于go - Go 如何以及何时为有界队列 channel 分配内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65417280/

    相关文章:

    Golang 和 DDD 域建模

    arrays - 没有malloc的动态数组?

    c - 分配并初始化大内存空间

    go - 如何使用 pprof 工具分析基准?

    go - 如何解释 go pprof/mutex 显示等待解锁?

    go - Go程序中如何使用pprof

    parsing - 如何将多个数据对象传递给 Golang 中的 HTML 模板

    go - 从 Go 闭包返回一个方法

    IOSurface 在 iOS 12 及以上版本逐渐增加内存

    mysql - sql.NullInt64 转 JSON