起因:
在做DIFF流量回放的平台的过程中,遇到了需要使用golang-set包的set这个数据结构,引发了内存泄漏。
经过:
在测试环境测试过程中,发现任务状态扭转到这份代码块的时候,内存会增加且不回退。通过排查定位发现问题代码如下:
//声明一个set
tempSet := set.NewSet()
//增加元素
tempSet.Add("aaaa")
tempSet.Add("bbbb")
//循环迭代
for value := range tempSet.Iterator().C {
if strings.Contains(value.(string), "aaaa") {
return
}
}
如何解决:
方案一:不要在遍历迭代器的过程中,提前退出。
方案二:在退出循环的时候,调用迭代器停止的方法。
代码如下:
//声明一个set
tempSet := set.NewSet()
//增加元素
tempSet.Add("aaaa")
tempSet.Add("bbbb")
//循环迭代
for value := range tempSet.Iterator().C {
if strings.Contains(value.(string), "aaaa") {
tempSet.Iterator().stop()
return
}
}
内存泄露如何产生:
关注到如下代码:
func (set *threadSafeSet) Iterator() *Iterator {
iterator, ch, stopCh := newIterator()
go func() {
set.RLock()
L:
for elem := range set.s {
select {
case <-stopCh:
break L
case ch <- elem:
}
}
close(ch)
set.RUnlock()
}()
return iterator
}
在创建迭代器的时候,封装者为了效率和性能,使用了协程和管道去实现的循环遍历。 当你使用迭代器的时候,遍历到了对应的元素之后提前结束就会导致该协程内存不被回收一直阻塞住。