【问题标题】:Why does my Go program's memory fluctuate so much?为什么我的 Go 程序的内存波动这么大?
【发布时间】:2014-01-11 15:57:37
【问题描述】:

我有一个分配大量地图和切片的 Go 程序。通常有很多使用、分配开销等。我运行它,它会加载大量数据,然后我使用 Web 服务对其进行查询。

在我让它运行后,当它读入所有数据并且没有进行任何查询(即应该是稳定的)时,我看到内存波动。最近有报道称:5.42 GB、5.01 GB 和 4.3 GB 的实内存。这是一个巨大的波动。

我有大约 1.5 亿个对象(切片挂在主哈希表上)。那是很多小物件。我预计会有一点波动(尽管当没有分配新对象并且主线程在套接字上阻塞时,我永远不会期望内存增加)。

可能的解释是

  • 大量小分配的开销只会增加任何自然波动
  • 一些代码正在分配对象(虽然我看不出如何)
  • Go GC 正在执行自己的分页 (?)
  • 我使用的是 Mac OS,但不知何故出现了问题

这种波动量是否正常/预期?

【问题讨论】:

  • 这个波动有多快?你能在这些内存使用测量上加上时间戳吗?
  • 超过 10 分钟。我不能在上面加上时间戳,但如果它很重要,我可以复制。
  • 不,我认为“分钟刻度”已经足够细化了。
  • 您是否尝试过在代码中使用profiler?它可以帮助您识别有大量分配的区域,以便您可以更改代码以避免它们。
  • 我可以,是的。我知道我正在分配很多对象,并且我知道我可以在分配方面做得更好。我的问题更多是关于我应该期望内存在分配后波动吗?

标签: memory-management go


【解决方案1】:

go-runtime 不会立即将未使用的内存释放到操作系统(可能很快会再次需要它)。 因此,查看操作系统级别,您只能看到整体情况的一部分。 使用http://golang.org/pkg/runtime/#ReadMemStats可以看到图片的另一部分。

pkg/runtime/malloc.goc 显示空闲列表,pkg/runtime/mgc0.c 显示垃圾收集器。

如果内存使用率在看似正常的稳定情况下下降,则在加载完成后,您可以强制进行 GC,并且您可能希望定期打印 Memstats 以获取更多信息。

【讨论】:

  • 谢谢,如果再次发生这种情况,我会查看 memstats。您知道为什么内存消耗会增加吗?
【解决方案2】:

波动可能是由于您的程序正在创建垃圾收集器最终必须收集的垃圾量。波动的频率将取决于您创建垃圾的次数/频率以及垃圾收集器何时收集垃圾。

变量是分配给栈还是堆是由编译器决定的。通常可以将指针、映射和切片分配给堆,但这只有在编译器的转义分析确定变量转义时才会发生。分配给堆的任何内容都需要进行垃圾回收。

尽管 Go 处理了堆栈与堆的细节,但尽可能少地创建垃圾会带来很大的好处。您可以阅读有关an extreme case where the garbage collector paused for 10 seconds 的信息。 Go 垃圾收集器并不完美,but it is improving。它改善得越少,您就不必担心它。但你至少应该意识到这一点。

您可以运行以下命令来确定编译器将分配给堆的内容:

go build -gcflags=-m program.go

您可能会对实际分配给堆的内容感到惊讶。例如,即使您在本地使用 bytes.Buffer,由于bytes.Buffer.buf being re-sliced,它仍然会被分配给堆。不管这是否应该发生,在某些情况下,您可能认为您没有制造任何垃圾,但实际上您确实如此。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-05
    • 2015-05-13
    • 1970-01-01
    • 2011-08-06
    • 1970-01-01
    相关资源
    最近更新 更多