我编写了一个golang程序,该程序在运行时使用1.2GB的内存。
调用将go tool pprof http://10.10.58.118:8601/debug/pprof/heap
导致转储仅使用323.4MB的堆。
- 其余的内存使用情况又如何呢?
- 有没有更好的工具来解释golang运行时内存?
使用gcvis
我得到这个:
..和此堆表单配置文件:
这是我的代码:https : //github.com/sharewind/push-server/blob/v3/broker
我编写了一个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
Answers:
堆配置文件显示活动内存,运行时认为该内存已被go程序使用(即:垃圾收集器尚未收集)。当GC确实收集内存时,配置文件会缩小,但不会将任何内存返回给系统。您将来的分配将尝试在请求系统提供更多信息之前使用先前收集的对象池中的内存。
从外部看,这意味着您程序的内存使用量将增加或保持不变。外部系统显示为程序的“驻留大小”的是分配给程序的RAM字节数,无论它是保存使用中的值还是收集的值。
这两个数字经常有很大差异的原因是:
如果要准确了解Go如何查看内存,可以使用运行时。ReadMemStats调用:http : //golang.org/pkg/runtime/#ReadMemStats
或者,如果您正在使用基于Web的分析,则可以通过以下浏览器访问分析数据:http://10.10.58.118:8601/debug/pprof/
单击堆链接,将显示堆配置文件的调试视图,该视图具有运行时的打印输出。底部。
runtime.MemStats文档(http://golang.org/pkg/runtime/#MemStats)提供了所有字段的说明,但有趣的是:
Sys与操作系统报告之间仍然存在差异,因为Go对系统的要求以及操作系统提供的内容并不总是相同的。此外,go不会跟踪CGO / syscall(例如:malloc / mmap)内存。
/debug/pprof/heap
不包含运行时的打印输出
我一直对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主页->左上角->数据源->添加新数据源):
从https://grafana.com导入仪表板#3242(Grafana主页->左上角->仪表板->导入):
最后,启动您的应用程序:它将运行时指标传输到contenerized Influxdb
。将您的应用程序置于合理的负载下(在我的情况下,它很小-5 RPS持续了几个小时)。
内存消耗分析
Sys
的(RSS
曲线的同义词)与曲线非常相似HeapSys
。事实证明,动态内存分配是整体内存增长的主要因素,因此堆栈变量所消耗的少量内存似乎是恒定的,可以忽略不计。HeapIdle
增长率与相同Sys
,而HeapReleased
始终为零。显然,运行时不返回内存OS所有,至少在这个试验的条件下: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%的内存。
您还可以使用StackImpact,它会自动记录常规的和异常触发的内存分配配置文件,并将其报告给仪表板,这些配置文件以历史记录和可比较的形式提供。有关更多详细信息,请参见此博客文章。Production Go应用程序中的内存泄漏检测
免责声明:我为StackImpact工作
--alloc_space
,不适用于内存泄漏检测。它只会显示自程序启动以来分配了多少内存。对于长时间运行的程序,该数字可能会很高。到目前为止,我们还没有发现StackImpact代理中有任何内存泄漏。