如何分析golang内存?

时间:2014-07-21 10:43:04

标签: performance memory go profile

我编写了一个golang程序,它在运行时使用了1.2GB的内存。

调用go tool pprof http://10.10.58.118:8601/debug/pprof/heap会导致只有323.4MB堆使用量的转储。

  • 内存使用的其余部分是什么?
  • 有没有更好的工具来解释golang运行时内存?

使用gcvis我明白了:

enter image description here

..和这个堆表单配置文件:

enter image description here

这是我的代码:https://github.com/sharewind/push-server/blob/v3/broker

4 个答案:

答案 0 :(得分:51)

堆配置文件显示活动内存,运行时认为正由go程序使用的内存(即:还没有被垃圾收集器收集)。当GC收集内存时,配置文件会缩小,但没有内存返回系统。在向系统询问更多内容之前,您将来的分配将尝试使用先前收集的对象池中的内存。

从外部来看,这意味着您的程序的内存使用将会增加或保持水平。外部系统作为"居民规模"你的程序是指分配给程序的RAM的字节数,无论它是否保留使用中的值或收集的值。

这两个数字通常完全不同的原因是:

  1. GC采集存储器对程序的外部视图没有影响
  2. 内存碎片
  3. 仅当使用的内存在上一个GC之后使用的内存加倍时,GC才会运行(默认情况下,请参阅:http://golang.org/pkg/runtime/#pkg-overview
  4. 如果您想要准确分解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)对所有字段都有解释,但讨论的有趣内容是:

    • HeapAlloc:本质上是分析器为您提供的内容(活动堆内存)
    • Alloc:类似于HeapAlloc,但对于所有人来说都是管理内存
    • Sys:从操作系统请求的内存总量(地址空间)

    系统管理系统之间仍然存在差异,操作系统会报告,因为系统的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个容器中运行GrafanaDocker

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

设置GrafanaInfluxDB Grafana之间的互动(Grafana主页 - >左上角 - >数据源 - >添加新数据源):

enter image description here

#3242导入信息中心https://grafana.com(Grafana主页 - >左上角 - >信息中心 - >导入):

enter image description here

最后,启动您的应用程序:它会将运行时指标传输到有争议的Influxdb。将您的应用程序置于合理的负载下(在我的情况下,它非常小 - 几个小时的5 RPS)。

内存消耗分析

  1. SysRSS)曲线的同义词非常类似于HeapSys曲线。事实证明,动态内存分配是整体内存增长的主要因素,因此堆栈变量消耗的少量内存似乎是不变的,可以忽略不计;
  2. 恒定数量的goroutines保证没有goroutine泄漏/堆栈变量泄漏;
  3. 在流程的整个生命周期中,分配的对象总量保持不变(没有考虑到波动的重要性)。
  4. 最令人惊讶的事实是:HeapIdle的增长速度与Sys相同,而HeapReleased始终为零。显然运行时并没有将内存返回到操作系统所有,至少在这个测试的条件下:
  5. 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.
    

    enter image description here enter image description here

    对于那些试图调查内存消耗问题的人,我建议您按照上述步骤排除一些微不足道的错误(如goroutine泄漏)。

    明确释放内存

    有趣的是,通过显式调用debug.FreeOSMemory(),可以显着减少内存消耗:

    // in the top-level package
    func init() {
       go func() {
           t := time.Tick(time.Second)
           for {
               <-t
               debug.FreeOSMemory()
           }
       }()
    }
    

    comparison

    事实上,与默认条件相比,这种方法节省了大约35%的内存。

答案 3 :(得分:5)

您还可以使用StackImpact,它会自动记录并向仪表板报告常规和异常触发的内存分配配置文件,这些配置文件以历史和可比较的形式提供。有关详细信息Memory Leak Detection in Production Go Applications

,请参阅此博文

enter image description here

免责声明:我为StackImpact工作