## 影響狀況## 服務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)} }