为了账号安全,请及时绑定邮箱和手机立即绑定

Go 如何以及何时为有界队列通道分配内存?

Go 如何以及何时为有界队列通道分配内存?

Go
偶然的你 2022-07-25 10:33:08
我正在使用 Go 的 pprof 工具来调查我的服务的内存使用情况。几乎所有的内存使用都来自一个设置多个有界队列通道的函数。我对 pprof 在这里告诉我的内容有些困惑:$ go tool pprof ~/pprof/pprof.server.alloc_objects.alloc_space.inuse_objects.inuse_space.007.pb.gzFile: serverType: inuse_spaceTime: Dec 21, 2020 at 10:46am (PST)Entering interactive mode (type "help" for commands, "o" for options)(pprof) list fooTotal: 102.73MBROUTINE ======================== 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,但这些行做的是同样的事情——它们创建具有相同输入类型和相同容量的缓冲通道。这是 pprof 进行随机抽样这一事实的产物吗?Go 是否会延迟分配通道(fwiw,在让服务运行几天后,这些值最终会均衡)?pprof 是否报告了沿通道发送的对象所需的内存以及通道本身所需的内存?
查看完整描述

1 回答

?
ITMISS

TA贡献1871条经验 获得超8个赞

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

Go 急切地分配通道内存

承诺的文档_make

通道的缓冲区使用指定的缓冲区容量进行初始化。

我查看了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 个通道分配内存。在每次内存分配调用中,Go 都有机会记录堆配置文件。平均而言,Go 将每 512kB 记录一次堆配置文件(runtime.MemProfileRate 的默认值)。由于这些通道的总大小为 488kB,平均而言,我们预计每次foo调用只记录一个分配。我上面分享的配置文件是在服务重启后相对较快的,所以分配字节数的差异是纯统计差异的结果。让服务运行一天后,配置文件稳定下来,显示第 142 行和第 146 行分配的字节数相等。


查看完整回答
反对 回复 2022-07-25
  • 1 回答
  • 0 关注
  • 60 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信