Go语言 如何分析戈朗人的记忆?

vsdwdz23  于 2022-12-07  发布在  Go
关注(0)|答案(7)|浏览(155)

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

7cjasjjr

7cjasjjr1#

堆配置文件显示活动内存,即运行时认为正在被go程序使用的内存(即:垃圾收集器没有收集内存)。当GC收集内存时,配置文件会收缩,但没有内存返回给系统。您将来的分配将尝试使用以前收集的对象池中的内存,然后再向系统请求更多内存。
从外部看,这意味着程序的内存使用量要么增加,要么保持不变。外部系统表示的程序的“驻留大小”是分配给程序的RAM字节数,无论它是保存使用中的go值还是收集的值。
这两个数字常常相差很大的原因是:

  1. GC收集内存对程序的外部视图没有影响
    1.内存碎片
  2. GC仅在当前使用的内存是上一次GC之后的两倍时运行(默认情况下,请参见:http://golang.org/pkg/runtime/#pkg-overview)
    如果你想知道Go语言是如何查看内存的,你可以使用runtime.ReadMemStats调用:http://golang.org/pkg/runtime/#ReadMemStats
    或者,由于您使用的是基于Web的分析,因此如果您可以通过浏览器访问分析数据,请访问:http://10.10.58.118:8601/debug/pprof/,单击heap链接将显示heap概要文件的调试视图,该视图的底部有一个runtime.MemStats结构的打印输出。
    运行时.MemStats文档(http://golang.org/pkg/runtime/#MemStats)对所有字段都有解释,但在本讨论中有趣的是:
  • HeapAlloc:实质上是分析器提供给您的内容(活动堆内存)
  • Alloc:类似于HeapAlloc,但适用于所有Go托管内存
  • Sys:从操作系统请求的内存(地址空间)总量

Sys和操作系统报告的内容之间仍然存在差异,因为Go语言对系统的要求和操作系统提供的内容并不总是相同的。malloc / mmap)内存不会被go跟踪。

bbuxkriu

bbuxkriu2#

作为对@Cookie of Nine答案的补充,简而言之:您可以尝试--alloc_space选项。
go tool pprof默认使用--inuse_space。它对内存使用情况进行采样,因此结果是真实的的子集。
通过--alloc_space,pprof返回自程序启动以来分配的所有内存。

u0sqgete

u0sqgete3#

我一直对我的Go应用程序不断增长的驻留内存感到困惑,最后我不得不学习Go生态系统中的分析工具。Runtime在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
    }
}

Docker容器中运行InfluxDBGrafana

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

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

https://grafana.com导入 Jmeter 板#3242(Grafana主页-〉左上角-〉 Jmeter 板-〉导入):

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

内存消耗分析

  1. SysRSS的同义词)曲线与HeapSys曲线非常相似,结果表明动态内存分配是内存增长的主要因素,因此堆栈变量消耗的少量内存似乎是恒定的,可以忽略不计;
    1.恒定的goroutine数量保证了没有goroutine泄漏/栈变量泄漏;
    1.在进程的生存期内,已分配对象的总量保持不变(没有必要考虑波动)。
    1.最令人吃惊的事实: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 leak)。

显式释放内存

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

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

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

ssm49v7z

ssm49v7z4#

您还可以使用StackImpact,它会自动记录常规和异常触发的内存分配配置文件并将其报告给 Jmeter 板,这些配置文件以历史和可比较的形式提供。

免责声明:我为StackImpact工作

vxf3dgd4

vxf3dgd45#

尝试回答以下原始问题

是否有更好的工具来解释golang运行时内存?

我发现以下工具很有用
Statsview https://github.com/go-echarts/statsview Statsview集成了标准的net/http/pprof
统计数据

v8wbuo2f

v8wbuo2f6#

这篇文章对你的问题很有帮助。
https://medium.com/safetycultureengineering/analyzing-and-improving-memory-usage-in-go-46be8c3be0a8
我运行了一个pprof分析。pprof是一个内置于Go语言中的工具,它可以对从运行中的应用程序中收集到的评测数据进行分析和可视化。它是一个非常有用的工具,可以从运行中的Go应用程序中收集数据,是性能分析的一个很好的起点。我建议在生产环境中运行pprof,这样你就可以获得客户正在做什么的真实样本。
当你运行pprof的时候,你会得到一些文件,这些文件根据你的配置,集中在goroutine,CPU,内存使用和其他一些东西上。我们将集中在堆文件上,挖掘内存和GC的统计信息。我喜欢在浏览器中查看pprof,因为我发现它更容易找到可操作的数据点。你可以用下面的命令来做到这一点。
go tool pprof -http=:8080 profile_name-heap.pb.gz
pprof也有一个CLI工具,但我更喜欢浏览器选项,因为我发现它更容易导航。我个人的建议是使用火焰图。我发现它是最容易理解的可视化工具,所以我大多数时候使用该视图。火焰图是函数堆栈跟踪的可视化版本。顶部的函数是被调用的函数,它下面的所有内容都是在函数执行过程中调用的。你可以点击单个函数调用来放大它们,这会改变视图。这让你更深入地了解特定函数的执行过程,这真的很有帮助。请注意,火焰图显示了消耗资源最多的函数,因此一些函数将不在那里。这使得更容易找出最大的瓶颈在哪里。
这有帮助吗?

cngwdvgl

cngwdvgl7#

尝试GO插件Tracy. Tracy是“真实的,纳秒分辨率,远程遥测”(...). GoTracy(插件的名称)是与Tracy连接的代理,并发送必要的信息,以更好地了解您的应用程序过程.导入插件后,您可以把遥测代码像下面的描述:

func exampleFunction() {
    gotracy.TracyInit()
    gotracy.TracySetThreadName("exampleFunction")
    for i := 0.0; i < math.Pi; i += 0.1 {

        zoneid := gotracy.TracyZoneBegin("Calculating Sin(x) Zone", 0xF0F0F0)
        gotracy.TracyFrameMarkStart("Calculating sin(x)")
        sin := math.Sin(i)
        gotracy.TracyFrameMarkEnd("Calculating sin(x)")
        gotracy.TracyMessageLC("Sin(x) = "+strconv.FormatFloat(sin, 'E', -1, 64), 0xFF0F0F)
        gotracy.TracyPlotDouble("sin(x)", sin)
        gotracy.TracyZoneEnd(zoneid)

        gotracy.TracyFrameMark()
    }
}

的结果类似于:

插件位于:https://github.com/grzesl/gotracy
翠丝被放置在:https://github.com/wolfpld/tracy

相关问题