## 影响状况## 服务A内存泄露,形成服务器内存不足,系统运行的服务A 由于OOM 被强制KILL 掉、致使任务丢失 ##分析思路## Golang 编写的服务遇到OOM状况如何分析处理那?首先咱们利用golang 自带的pprof来分析。在main.go中 增长` go func() { if err := http.ListenAndServe("0.0.0.0:12345", nil); err != nil { log.Println(err) } }()`
而后再结合火焰图 去分析<如下是两张火焰图>golang
图1 sql
图2数据库
从图1 和图2 得知-sql map 有内存泄露 sql 也有内存泄露、 目前260w 的数据,天天跑N次。 每次会切割成M次子任务、 因此会泄露N*M次
因为经过火焰图咱们追踪到了泄露的Func ,那么咱们直接定位到相应源码去查看下·安全
从图中能够看出、这个清空操做失效、由于time.Now().Day() 永远和w.FrequencyControlFormat.Day 一致。另外每次new 都是新的引用、新的地址、致使每次都会将数据库数据全量load到map内存中,不会被释放、服务器
另外咱们再经过debug/pprof 去查看下各项详细状况并发
能够看出goroutine 数量一直在增长,没有被释放掉。由于channel 没有主动关闭、致使还会夯住对应的goroutine,因此要注意channel 的使用要及时关闭,不然也会形成资源的浪费、spa
那么咱们在分析一下并发安全的map 里面存储的数据为何是引用的数据库的数据源地址debug
func (m *Map) Store(key, value interface{}) { // 若是read存在这个键,而且这个entry没有被标记删除,尝试直接写入,写入成功,则结束 // 第一次检测 read, _ := m.read.Load().(readOnly) if e, ok := read.m[key]; ok && e.tryStore(&value) { return } // dirty map锁 m.mu.Lock() // 第二次检测 read, _ = m.read.Load().(readOnly) if e, ok := read.m[key]; ok { // unexpungelocc确保元素没有被标记为删除 // 判断元素被标识为删除 if e.unexpungeLocked() { // 这个元素以前被删除了,这意味着有一个非nil的dirty,这个元素不在里面. m.dirty[key] = e } // 更新read map 元素值 e.storeLocked(&value) } else if e, ok := m.dirty[key]; ok { // 此时read map没有该元素,可是dirty map有该元素,并需修改dirty map元素值为最新值 e.storeLocked(&value) } else { // read.amended==false,说明dirty map为空,须要将read map 复制一份到dirty map if !read.amended { m.dirtyLocked() // 设置read.amended==true,说明dirty map有数据 m.read.Store(readOnly{m: read.m, amended: true}) } // 设置元素进入dirty map,此时dirty map拥有read map和最新设置的元素 m.dirty[key] = newEntry(value) } // 解锁,有人认为锁的范围有点大,假设read map数据很大,那么执行m.dirtyLocked()会耗费花时间较多,彻底能够在操做dirty map时才加锁,这样的想法是不对的,由于m.dirtyLocked()中有写入操做 m.mu.Unlock() } //重点在这里 引用- //致使forrange 数据库的数据的时候 -数据库的数据不能被释放~ func newEntry(i interface{}) *entry { return &entry{p: unsafe.Pointer(&i)} }