Golang WaitGroup导致内存泄漏,我该怎么做才能改进这个功能

时间:2015-08-11 13:11:09

标签: memory-leaks go

我一直在努力在我们的应用程序中发现内存泄漏,并且一直在使用pprof工具来了解正在发生的事情。

当我查看堆时,我经常看到以下函数,我不明白为什么(或者如果)它实际上是一个问题。

func CreateClients(raw []byte) bool {

    macs := []string{}
    conn := FormatConn(raw)

    if conn.Ap_Mac != "" {

        var wg sync.WaitGroup
        var array []Client

        c1 := make(chan Client)

        clients := FormatClients(conn)

        wg.Add(len(clients))

        for _, c := range clients {
            go func(d Client) {
                defer wg.Done()
                c1 <- UpdateClients(d)
            }(c)
        }

        go func() {
            defer wg.Done()
            for {
                select {
                case client := <-c1:
                    array = append(array, client)
                    macs = append(macs, client.Client_Mac)
                }
            }
        }()

        wg.Wait()
        // Do some other stuff
    ...
}

UpdateClients函数更新Mongo中的客户端模型。当它返回时,我需要每个客户端 - 所以我可以用ES索引它,我需要一个mac数组来做其他的事情。

我已经浏览了在线示例,并认为这是循环通道的推荐方式。

我的pprof堆看起来像这样,并在几天内稳步增长:

7.53MB of 9.53MB total (79.00%)
Dropped 234 nodes (cum <= 0.05MB)
Showing top 5 nodes out of 28 (cum >= 1MB)
      flat  flat%   sum%        cum   cum%
       2MB 21.00% 21.00%        2MB 21.00%  strings.Replace
    1.51MB 15.89% 36.89%     1.51MB 15.89%  github.com/PolkaSpots/worker/worker.func·006
    1.51MB 15.87% 52.76%     1.51MB 15.87%  github.com/PolkaSpots/worker/worker.func·008
    1.50MB 15.75% 68.51%     1.50MB 15.75%  newproc_m
       1MB 10.50% 79.00%        1MB 10.50%  gopkg.in/mgo.v2/bson.(*decoder).readStr

是否有更有效/推荐的方法来实现这一目标?

修改_

正如所建议的那样,我已经改变了循环

    done := make(chan bool)

    go func() {
        for {
            select {
            case client := <-c1:
                array = append(array, client)
                macs = append(macs, client.Client_Mac)
            case <-done:
                return
            }
        }
    }()

    wg.Wait()
    close(done)

1 个答案:

答案 0 :(得分:4)

接收循环永不中断:

  for {
            select {
            case client := <-c1:
            ...
   }

它没有停止条件,没有超时,没有 - 所以它会永远挂在那里 - 即使你的整个功能退出。它会泄漏goroutine和通道。

最重要的是,当此循环退出时,您将推迟wg.Done,但是您没有wg.Add来匹配它。因此,如果这个循环退出,你会感到恐慌。

您需要做的是找到一些方法来停止for / select循环。最简单的方法IMO - 添加第二个频道,该频道在wg.Wait()之后接收数据,但在该goroutine中不执行wg.Done()