我编写了一个golang程序,它在运行时使用了1.2GB的内存。
调用go tool pprof http://10.10.58.118:8601/debug/pprof/heap
会导致只有323.4MB堆使用量的转储。
使用gcvis
我明白了:
..和这个堆表单配置文件:
这是我的代码:https://github.com/sharewind/push-server/blob/v3/broker
答案 0 :(得分:51)
堆配置文件显示活动内存,运行时认为正由go程序使用的内存(即:还没有被垃圾收集器收集)。当GC收集内存时,配置文件会缩小,但没有内存返回系统。在向系统询问更多内容之前,您将来的分配将尝试使用先前收集的对象池中的内存。
从外部来看,这意味着您的程序的内存使用将会增加或保持水平。外部系统作为"居民规模"你的程序是指分配给程序的RAM的字节数,无论它是否保留使用中的值或收集的值。
这两个数字通常完全不同的原因是:
如果您想要准确分解Go如何看待内存,可以使用runtime.ReadMemStats调用:http://golang.org/pkg/runtime/#ReadMemStats
或者,如果您可以通过浏览器访问基于Web的分析:http://10.10.58.118:8601/debug/pprof/
,则单击堆链接将显示堆配置文件的调试视图,该视图具有打印输出底部的runtime.MemStats结构。
runtime.MemStats文档(http://golang.org/pkg/runtime/#MemStats)对所有字段都有解释,但讨论的有趣内容是:
系统管理系统之间仍然存在差异,操作系统会报告,因为系统的Go问题以及操作系统提供的内容并不总是相同的。此外,没有跟踪CGO /系统调用(例如:malloc / mmap)内存。
答案 1 :(得分:23)
作为@Cookie of Nine的答案的补充,简而言之:您可以尝试--alloc_space
选项。
go tool pprof
默认使用--inuse_space
。它对内存使用情况进行抽样,因此结果是实际内容的子集
通过--alloc_space
pprof返回自程序启动以来所有分配的内存。
答案 2 :(得分:12)
我总是对Go应用程序不断增长的住宅内存感到困惑,最后我不得不学习Go生态系统中存在的分析工具。运行时在runtime.Memstats结构中提供了许多指标,但可能很难理解哪些指标可以帮助找出内存增长的原因,因此需要一些额外的工具。
分析环境
在您的应用中使用https://github.com/tevjef/go-runtime-metrics。例如,您可以将其放在main
:
import(
metrics "github.com/tevjef/go-runtime-metrics"
)
func main() {
//...
metrics.DefaultConfig.CollectionInterval = time.Second
if err := metrics.RunCollector(metrics.DefaultConfig); err != nil {
// handle error
}
}
在InfluxDB
个容器中运行Grafana
和Docker
:
docker run --name influxdb -d -p 8086:8086 influxdb
docker run -d -p 9090:3000/tcp --link influxdb --name=grafana grafana/grafana:4.1.0
设置Grafana
和InfluxDB
Grafana
之间的互动(Grafana主页 - >左上角 - >数据源 - >添加新数据源):
从#3242导入信息中心https://grafana.com(Grafana主页 - >左上角 - >信息中心 - >导入):
最后,启动您的应用程序:它会将运行时指标传输到有争议的Influxdb
。将您的应用程序置于合理的负载下(在我的情况下,它非常小 - 几个小时的5 RPS)。
内存消耗分析
Sys
(RSS
)曲线的同义词非常类似于HeapSys
曲线。事实证明,动态内存分配是整体内存增长的主要因素,因此堆栈变量消耗的少量内存似乎是不变的,可以忽略不计; HeapIdle
的增长速度与Sys
相同,而HeapReleased
始终为零。显然运行时并没有将内存返回到操作系统所有,至少在这个测试的条件下:HeapIdle minus HeapReleased estimates the amount of memory that could be returned to the OS, but is being retained by the runtime so it can grow the heap without requesting more memory from the OS.
对于那些试图调查内存消耗问题的人,我建议您按照上述步骤排除一些微不足道的错误(如goroutine泄漏)。
明确释放内存
有趣的是,通过显式调用debug.FreeOSMemory()
,可以显着减少内存消耗:
// in the top-level package
func init() {
go func() {
t := time.Tick(time.Second)
for {
<-t
debug.FreeOSMemory()
}
}()
}
事实上,与默认条件相比,这种方法节省了大约35%的内存。
答案 3 :(得分:5)
您还可以使用StackImpact,它会自动记录并向仪表板报告常规和异常触发的内存分配配置文件,这些配置文件以历史和可比较的形式提供。有关详细信息Memory Leak Detection in Production Go Applications
,请参阅此博文免责声明:我为StackImpact工作