【问题标题】:Go: memory issuesGo:内存问题
【发布时间】:2017-03-23 14:13:49
【问题描述】:

我需要你的智慧。

我有一个用 Go 编写的巨大守护进程。前段时间有用户报告说代码某处可能存在内存泄漏。

我开始调查这个问题。当主要代码检查没有让我找到有关此泄漏性质的任何线索时,我试图专注于我的流程是如何工作的。

我的想法很简单:如果我未能删除对某些对象的引用,我的堆应该会不断增长。我编写了以下程序来监控堆:

func PrintHeap() {
    ticker := time.NewTicker(time.Second * 5)
    for {
        <-ticker.C
        st := &runtime.MemStats{}
        runtime.ReadMemStats(st)
        // From Golang docs: HeapObjects increases as objects are allocated
        // and decreases as the heap is swept and unreachable objects are
        // freed.
        fmt.Println("Heap allocs:", st.Mallocs, "Heap frees:",
            st.Frees, "Heap objects:", st.HeapObjects)
    }
}

此过程每 5 秒打印一些有关堆的信息,包括当前分配的对象数。

现在谈谈守护进程的作用。它处理来自某些 UDP 输入的行。每行都包含有关某个 HTTP 请求的一些信息,并被解析为典型的 Go 结构。这个结构有一些数字和字符串字段,包括一个请求路径。然后这个结构发生了很多事情,但这些事情在这里无关紧要。

现在,我将输入速率设置为每秒 1500 行,每行都相当短(您可以将其解读为:使用标准请求路径,/)。

运行应用程序后,我可以看到堆大小在某个时间点稳定:

Heap allocs: 180301314 Heap frees: 175991675 Heap objects: 4309639
Heap allocs: 180417372 Heap frees: 176071946 Heap objects: 4345426
Heap allocs: 180526254 Heap frees: 176216276 Heap objects: 4309978
Heap allocs: 182406470 Heap frees: 177496675 Heap objects: 4909795
Heap allocs: 183190214 Heap frees: 178248365 Heap objects: 4941849
Heap allocs: 183302680 Heap frees: 178958823 Heap objects: 4343857
Heap allocs: 183412388 Heap frees: 179101276 Heap objects: 4311112
Heap allocs: 183528654 Heap frees: 179181897 Heap objects: 4346757
Heap allocs: 183638282 Heap frees: 179327221 Heap objects: 4311061
Heap allocs: 185609758 Heap frees: 181330408 Heap objects: 4279350

当达到这个状态时,内存消耗停止增长。

现在,我更改了输入,使每行的长度超过 2k 个字符(带有巨大的 /AAAAA... 请求路径),这就是奇怪的事情开始发生的地方。

堆大小急剧增长,但一段时间后仍变得稳定:

Heap allocs: 18353000513 Heap frees: 18335783660 Heap objects: 17216853
Heap allocs: 18353108590 Heap frees: 18335797883 Heap objects: 17310707
Heap allocs: 18355134995 Heap frees: 18336081878 Heap objects: 19053117
Heap allocs: 18356826170 Heap frees: 18336182205 Heap objects: 20643965
Heap allocs: 18366029630 Heap frees: 18336925394 Heap objects: 29104236
Heap allocs: 18366122614 Heap frees: 18336937295 Heap objects: 29185319
Heap allocs: 18367840866 Heap frees: 18337205638 Heap objects: 30635228
Heap allocs: 18368909002 Heap frees: 18337309215 Heap objects: 31599787
Heap allocs: 18369628204 Heap frees: 18337362196 Heap objects: 32266008
Heap allocs: 18373482440 Heap frees: 18358282964 Heap objects: 15199476
Heap allocs: 18374488754 Heap frees: 18358330954 Heap objects: 16157800

但是内存消耗却在不断增长,而且从未停止过。我的问题是:关于发生了什么的任何想法?

我想过由于大量巨大的对象而导致的内存碎片,但实际上我真的不知道该怎么想。

【问题讨论】:

  • 尝试打印runtime.NumGoroutine。 Goroutine 泄漏是迄今为止最常见的内存消耗问题。
  • 但是没有创建新的 goroutines,请求被放入一个循环缓冲区,该缓冲区被一个相同的例程定期清空。但是,是的,我会努力的。
  • 尝试打印 st.Sys?
  • 你使用哪个 go 版本?我在 1.3 和 1.4 中遇到了类似的问题
  • @apxp 是 go 版本 go1.7.4 linux/amd64

标签: go memory heap-memory


【解决方案1】:

你可以试试 go 内存分析工具。

首先您需要更改您的程序,以便它提供内存配置文件。有几种方法。

  1. 如果您可以发布该端点,您可以使用包net/http/pprof 参见https://golang.org/pkg/net/http/pprof/
  2. 您可以使用包runtime/pprof 并让您的程序将内存配置文件转储到已知位置,以响应接收信号等特定事件。

之后,您可以使用go tool pprof 分析内存配置文件 如果您选择将内存配置文件转储到文件,您可以调用 go tool pprof &lt;path/to/executable&gt; &lt;file&gt; 或调用 go tool pprof &lt;path/to/executable&gt; http://&lt;host&gt;:&lt;port&gt;/debug/pprof/heap,如果您使用 net/http/pprof 并使用 top5 获取前 5 个函数,这些函数分配了大部分内存。您可以对特定功能使用list 命令来查看哪些行分配了多少内存。

从那开始,你应该能够推断出内存的增加 你在观察。

您还可以在https://blog.golang.org/profiling-go-programs 上阅读有关此内容的信息,其中还介绍了如何分析您的 CPU 使用情况。只需搜索单词memprofile 即可跳转到相关部分。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-10
    • 2010-09-22
    • 2014-12-19
    • 2021-10-06
    • 2015-05-03
    相关资源
    最近更新 更多