英文:
Why does my Go program's memory fluctuate so much?
问题
我有一个Go程序,它分配了大量的映射和切片。通常会有很多使用情况、分配开销等。我运行它,它加载了大量的数据,然后我通过一个Web服务进行查询。
在我让它运行后,当它读入了所有的数据并且没有进行任何查询时(即应该是稳定的),我看到内存有波动。最近报告的是:5.42 GB、5.01 GB和4.3 GB的实际内存。这是一个巨大的波动。
我有大约1.5亿个对象(挂在主哈希表上的切片)。这是很多小对象。我期望会有一些波动(尽管我从不希望在没有分配新对象且主线程/线程正在阻塞在套接字上时内存会增加)。
可能的解释是:
- 大量小分配的开销只会增加任何自然波动
- 一些代码正在分配对象(尽管我看不出来是怎么回事)
- Go的垃圾回收正在进行自己的分页(?)
- 我使用的是Mac OS,可能是它的问题
这种波动的程度是否正常/预期?
英文:
I have a Go program that allocates lots of maps and slices. Generally a lot of usage, allocation overhead etc. I run it, it loads a lot of data in, and then I query it with a web service.
After I leave it running, when it's read in all its data and isn't doing any queries (i.e should be stable) I see memory fluctuations. Recently it's reported: 5.42 GB, 5.01 GB and 4.3 GB of real memory. That's a massive fluctuation.
I have about 150 million objects (slices hanging off the main hashtable). That's a lot of little objects. I expect a little fluctuation (although I would never expect memory to increase when no new objects are being allocated and the main thread/s is blocking on a socket).
Possible explanations are
- the overhead of lots of small allocations just multiplies any natural fluctuation
- some code is allocating objects (although I can't see how)
- the Go GC is doing its own paging (?)
- I'm using Mac OS, and it's at fault somehow
Is this amount of fluctuation normal / expected?
答案1
得分: 1
go-runtime不会立即释放未使用的内存给操作系统(因为可能很快会再次需要)。
因此,从操作系统级别来看,你只能看到整体图片的一部分。
使用http://golang.org/pkg/runtime/#ReadMemStats,你可以看到图片的另一部分。
pkg/runtime/malloc.goc显示了空闲列表,pkg/runtime/mgc0.c显示了垃圾收集器。
如果在加载完成后,内存使用量在稳定的情况下下降,这似乎是正常的,你可以强制进行垃圾收集,并且可能希望定期打印Memstats以获取更多信息。
英文:
The go-runtime doesn't immediately release unused memory to the OS (it might be needed again soon).
So looking at the OS-level, you see only a part of the overall picture.
Using http://golang.org/pkg/runtime/#ReadMemStats you can see another part of the picture.
pkg/runtime/malloc.goc shows the freelist, and pkg/runtime/mgc0.c shows the garbage collector.
If memory-usage goes down in a stable situation that seems normal, after loading finishes, you can force a GC, and you might want to print the Memstats regularly for more info.
答案2
得分: 0
波动很可能是由于你的程序创建了大量垃圾,垃圾收集器最终需要清理掉。波动的频率取决于你创建垃圾的数量和频率,以及垃圾收集器的清理时间。
变量是分配到栈还是堆取决于编译器。通常指针、映射和切片可以分配到堆上,但只有在编译器的逃逸分析确定变量逃逸时才会这样分配。分配到堆上的任何内容都需要进行垃圾回收。
尽管Go处理了栈和堆的细节,但尽量减少垃圾的创建仍然非常有益。你可以阅读一个极端案例,其中垃圾收集器暂停了10秒钟。Go的垃圾收集器并不完美,但它正在改进。它改进得越多,你就越不用担心它。但你至少应该意识到这一点。
你可以运行以下命令来确定编译器将分配到堆上的内容:
go build -gcflags=-m program.go
你可能会对实际分配到堆上的内容感到惊讶。例如,即使你在本地使用了一个 bytes.Buffer,它仍然会被分配到堆上,这是因为 bytes.Buffer.buf 被重新切片。无论是否应该这样,可能会有一些情况让你认为自己没有创建任何垃圾,但实际上是有的。
英文:
The fluctuations are likely due to the amount of garbage your program is creating that the garbage collector has to eventually collect. The frequency of the fluctuation is going to depend on how much / how often you are creating garbage and when the garbage collector collects it.
Whether a variable is allocated to the stack or the heap is determined by the compiler. Generally pointers, maps, and slices can be allocated to the heap, but this only happens if the compiler's escape analysis determines that a variable escapes. Anything that is allocated to the heap will need to be garbage collected.
Even though Go handles the stack vs heap details, creating as little garbage as possible can be of great benefit. You can read about an extreme case where the garbage collector paused for 10 seconds. The Go garbage collector isn't perfect, but it is improving. The more it improves less you'll have to worry about it. But you should at least be aware of it.
You can run the following to determine what the compiler will allocate to the heap:
go build -gcflags=-m program.go
You may be surprised by what actually gets allocated to the heap. For example, even if you use a bytes.Buffer locally it still gets allocated to the heap, due to bytes.Buffer.buf being re-sliced. Regardless of whether that's supposed to happen or not, there may be situations where you think you're not creating any garbage, but in reality you are.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论