为什么我的Go程序的内存波动如此之大?

时间:2014-01-11 15:57:37

标签: memory-management go

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

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

我有大约1.5亿个对象(切片悬挂在主哈希表上)。这是很多小东西。我预计会有一点波动(虽然我没想到在没有分配新对象且主线程在套接字上阻塞时内存会增加。)

可能的解释是

  • 许多小额分配的开销只会增加任何自然波动
  • 一些代码正在分配对象(虽然我看不清楚)
  • Go GC正在进行自己的分页(?)
  • 我正在使用Mac OS,而且它有点错误

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

2 个答案:

答案 0 :(得分:1)

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

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

如果内存使用情况在一个看似正常的稳定情况下发生变化,则在加载完成后,您可以强制执行GC,并且可能需要定期打印Memstats以获取更多信息。

答案 1 :(得分:0)

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

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

即使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,它仍然会分配给堆。无论是否应该发生这种情况,可能会出现这样的情况,即您认为自己并未创造任何垃圾,但事实上您确实存在垃圾。