全局map内存泄漏问题的排查和解决 | go 技术论坛-江南app体育官方入口
pods表现为内存持续增长
使用pprof定位到具体函数
找到对应代码
type ebus struct { eventchan chan *event // 内部事件 eventhandlers map[int]func(*event) subscribers map[string]map[subscriber]struct{} // 订阅者 running bool // 记录服务是否正在运行 } func (p *ebus) pub(e *event) { ... } // 订阅消息 func (p *ebus) sub(e *event) { if _, ok := p.subscribers[e.name]; !ok { p.subscribers[e.name] = make(map[subscriber]struct{}) p.subscribers[e.name][e.subscriber] = struct{}{} return } if _, ok := p.subscribers[e.name][e.subscriber]; ok { return } p.subscribers[e.name][e.subscriber] = struct{}{} } // 取消指定消息订阅 func (p *ebus) unsub(e *event) { ... delete(p.subscribers[e.name], e.subscriber) } // 取消所有消息订阅 func (p *ebus) unsuball(e *event) { ... delete(p.subscribers, e.name) }
分析问题
根据pprof能看出内存泄漏在sub函数
sub里面会分配内存的就只有p.subscribers
subscribers是一个map
可以看到unsub、unsuball都是删除map对应key的方法
首先怀疑是不是每正常调用这两个函数取消订阅,但是读完业务代码后发现是正常unsuball了的
最后想起来,map,delete key后是会释放value的空间,但是map自身的空间是不会被释放的
这是常见的全局map出现内存泄漏的问题验证问题
func printmemusage() { var m runtime.memstats runtime.readmemstats(&m) fmt.println("memory allocation:", m.alloc/1024/1024, "mb") } var mymap = make(map[int]int) func testmap(t *testing.t) { for i := 0; i < 1000000; i { mymap[i] = i } for i := 0; i < 1000000; i { delete(mymap, i) } // 触发垃圾收集并获取内存统计信息前的内存分配量 printmemusage() // 强制进行垃圾收集并读取内存统计信息 debug.freeosmemory() runtime.gc() // 打印对象创建后的内存分配量 printmemusage() }
修改代码:map放到函数内,即从全局map改为函数内mapfunc testmap(t *testing.t) { // 创建一个对象 var mymap = make(map[int]int) for i := 0; i < 1000000; i { mymap[i] = i } for i := 0; i < 1000000; i { delete(mymap, i) } ... ...
可以看到修改后的内存明显下降了怎么修复
全局map的内存泄漏是典型的问题
修复这个问题只能想办法让这个map被回收
我这里是加了个定时器,每分钟重置下map
本作品采用《cc 协议》,转载必须注明作者和本文链接
感觉用
sync.pool
来解决更好。我看你使用的是重建策略。经实测 把map定义放到函数内并没减少内存:
以下是测试结果:
虽然 map delete 后内存不会被释放,但是在后续继续写map时,这些内存是会被复用的。全局 map 泄露只能解释内存居高不下,但是不能解释持续增长。