我一直在努力在我们的应用程序中发现内存泄漏,并且一直在使用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)
答案 0 :(得分:4)
接收循环永不中断:
for {
select {
case client := <-c1:
...
}
它没有停止条件,没有超时,没有 - 所以它会永远挂在那里 - 即使你的整个功能退出。它会泄漏goroutine和通道。
最重要的是,当此循环退出时,您将推迟wg.Done
,但是您没有wg.Add
来匹配它。因此,如果这个循环退出,你会感到恐慌。
您需要做的是找到一些方法来停止for / select循环。最简单的方法IMO - 添加第二个频道,该频道在wg.Wait()
之后接收数据,但在该goroutine中不执行wg.Done()
。