我编写了一个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

堆配置文件显示活动内存,运行时认为该内存已被go程序使用(即:垃圾收集器尚未收集)。当GC确实收集内存时,配置文件会缩小,但是不会有内存返回给系统。您将来的分配将尝试在请求系统提供更多信息之前使用先前收集的对象池中的内存。

从外部看,这意味着您程序的内存使用量将增加或保持不变。外部系统显示为程序的"驻留大小"的是分配给程序的RAM字节数,无论它是保存使用中的值还是收集的值。

这两个数字经常有很大差异的原因是:

  • GC收集内存对程序的外部视图没有影响
  • 内存碎片
  • 仅当先前使用的内存在使用的内存增加一倍时,GC才会运行(默认情况下,请参见:http://golang.org/pkg/runtime/#pkg-overview)
  • 如果要准确了解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)提供了所有字段的说明,但有趣的是:

    • HeapAlloc:本质上是探查器为您提供的内容(活动堆内存)
    • Alloc:与HeapAlloc类似,但全部用于托管内存
    • Sys:操作系统请求的内存总量(地址空间)

    Sys与操作系统报告之间仍然存在差异,因为Go对系统的要求以及操作系统提供的内容并不总是相同的。另外,go不会跟踪CGO / syscall(例如:malloc / mmap)内存。

    简而言之,作为@Cookie of Nine答案的补充:您可以尝试--alloc_space选项。

    go tool pprof默认情况下使用--inuse_space。它对内存使用情况进行采样,因此结果是实数的子集。
    通过--alloc_space,pprof返回自程序启动以来所有分配的内存。

    我一直对Go应用程序的驻留内存不断增长感到困惑,最后我不得不学习Go生态系统中存在的性能分析工具。运行时在运行时.Memstats结构中提供了许多指标,但是可能很难理解其中哪些指标可以帮助找出内存增长的原因,因此需要一些其他工具。

    分析环境

    在您的应用程序中使用https://github.com/tevjef/go-runtime-metrics。例如,您可以将其放在main中:

    Docker容器中运行InfluxDBGrafana

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

    enter image description here

    从https://grafana.com导入仪表板#3242(Grafana主页->左上角->仪表板->导入):

    enter image description here

    最后,启动您的应用程序:它将运行时指标传输到统一的InfluxDB。将您的应用程序置于合理的负载下(在我的情况下,它很小-5 RPS持续了几个小时)。

    内存消耗分析

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

    comparison

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

    您还可以使用StackImpact,它会自动记录常规和异常触发的内存分配配置文件并将其报告给仪表板,这些配置文件以历史记录和可比较的形式提供。有关更多详细信息,请参见此博客文章。Production Go应用程序中的内存泄漏检测

    enter image description here

    免责声明:我为StackImpact工作