狠狠躁夜夜躁人人爽超碰97香蕉|色婷婷日日躁夜夜躁|亚洲一区欧美一区在线播|久久久久久性高|伊人久久大香线蕉亚洲

歡迎來到同城快修-附近家電維修、家電清洗、家電安裝服務平臺

24小時家電維修熱線:

400—1558638

當前位置:主頁 > 空調 > 維修資訊 >

內存出現故障怎么檢修(內存出現故障怎么檢修視頻)

發布日期:2022-12-15 10:09:47 瀏覽:
內存出現故障怎么檢修(內存出現故障怎么檢修視頻)

前沿拓展:


問題出現出現報警!!!

在日常搬磚的某一天發現了某微服務 bytedance.xiaoming 服務有一些實例內存過高,達到 80%。而這個服務很久沒有上線過新版本,所以可以排除新代碼上線引入的問題。

發現問題后,首先進行了遷移實例,除一臺實例留作問題排查外,其余實例進行了遷移,遷移過后新實例內存較低。但發現隨著時間推移,遷移過的實例內存也有緩慢增高的現象,有內存泄漏的表現。

問題定位推測一:懷疑是 goroutine 逃逸排查過程

通常內存泄露的主因就是 goroutine 過多,因此首先懷疑 goroutine 是否有問題,去看了 goroutine 發現很正常,總量較低且沒有持續增長現象。(當時忘記截圖了,后來補了一張圖,但是 goroutine 數量一直是沒有變化的)

排查結果

沒有 goroutine 逃逸問題。

推測二:懷疑代碼出現了內存泄露排查過程

通過 pprof 進行實時內存采集,對比問題實例和正常實例的內存使用狀況:

問題實例:

正常實例:

進一步看問題實例的 graph:

從中可以發現,metircs.flushClients()占用的內存是最多的,去定位源碼:

func (c *tagCache) Set(key []byte, tt *cachedTags) { if atomic.AddUint64(&c.setn, 1)&0x3fff == 0 { // every 0x3fff times call, we clear the map for memory leak issue // there is no reason to have so many tags // FIXME: sync.Map don't have Len method and `setn` may not equal to the len in concurrency env samples := make([]interface{}, 0, 3) c.m.Range(func(key interface{}, value interface{}) bool { c.m.Delete(key) if len(samples) < cap(samples) { samples = append(samples, key) } return true }) // clear map logfunc("[ERROR] gopkg/metrics: too many tags. samples: %v", samples) } c.m.Store(string(key), tt)}

發現里面為了規避內存泄露,已經通過計數的方式,定數清理掉 sync.Map 存儲的 key 了。理論上不應該出現問題。

排查結果

沒有代碼 bug 導致內存泄露的問題。

推測三:懷疑是 RSS 的問題排查過程

這時注意到了一個事情,在 pprof 里看到 metrics 總共只是占用了 72MB,而總的 heap 內存只有 170+MB 而我們的實例是 2GB 內存配置,占用 80%內存就意味著 1.6GB 左右的 RSS 占用,這兩個嚴重不符(這個問題的臨時解決方法在后文有介紹),這并不應該導致內存占用 80%報警。因此猜測是內存沒有及時回收導致的。

經過排查,發現了這個神奇的東西:

一直以來 go 的 runtime 在釋放內存返回到內核時,在 Linux 上使用的是 MADV_DONTNEED,雖然效率比較低,但是會讓 RSS(resident set size 常駐內存集)數量下降得很快。不過在 go 1.12 里專門針對這個做了優化,runtime 在釋放內存時,使用了更加高效的 MADV_FREE 而不是之前的 MADV_DONTNEED。詳細的介紹可以參考這里:

https://go-review.googlesource.com/c/go/+/135395/

go1.12 的更新原文:

Go 1.12~1.15 runtime 優化了 GC 策略,在 Linux 內核版本支持時 (> 4.5),會默認采用更『激進』的策略使得內存重用更高效、延遲更低等諸多優化。帶來的負面影響就是 RSS 并不會立刻下降,而是推遲到內存有一定壓力時。

我們的 go 版本是 1.15, 內核版本是 4.14,剛好中招!

排查結果

go 編譯器版本+系統內核版本命中了 go 的 runtime gc 策略,會使得在堆內存回收后,RSS 不下降。

問題解決解決方法

解決方法一共有兩種:

一種是在環境變量里指定GODEBUG=madvdontneed=1

這種方法可以強制 runtime 繼續使用 MADV_DONTNEED.(參考:https://github.com/golang/go/issues/28466)。但是啟動了madvise dontneed 之后,會觸發 TLB shootdown,以及更多的 page fault。延遲敏感的業務受到的影響可能會更大。因此這個環境變量需要謹慎使用!

2. 升級 go 編譯器版本到 1.16 以上

看到 go 1.16 的更新說明。已經放棄了這個 GC 策略,改為了及時釋放內存而不是等到內存有壓力時的惰性釋放。看來 go 官網也覺得及時釋放內存的方式更加可取,在多數的情況下都是更為合適的。

附:解決 pprof 看 heap 使用的內存小于 RSS 很多的問題,可以通過手動調用 debug.FreeOSMemory 來解決,但是執行這個操作是有代價的。

同時 go1.13 版本中 FreeOSMemory 也不起作用了(https://github.com/golang/go/issues/35858),推薦謹慎使用。

實施結果

我們選擇了方案二。在升級 go1.16 之后,實例沒有出現內存持續快速增長的現象。

再次用 pprof 去看實例情況,發現占用內存的函數也有變化。之前占用內存的 metrics.glob 已經降下去了。看來這個解決方法是有成效的。

遇到的其他坑

在排查過程中發現的另一個可能引起內存泄露的問題(本服務未命中),在未開啟 mesh 的情況下,kitc 的服務發現組件是有內存泄露的風險的。

從圖中可以看到 cache.(*Asynccache).refresher 占用內存較多,且隨著業務處理量的增多,其內存占用會一直不斷的增長。

很自然的可以想到是在新建 kiteclient 的時候,可能有重復構建 client 的情況出現。于是進行了代碼排查,并沒有發現重復構建的情況。但是去看 kitc 的源碼,可以發現,在服務發現時,kitc 里建立了一個緩存池 asyncache 來進行 instance 的存放。這個緩存池每 3 秒會刷新一次,刷新時調用 fetch,fetch 會進行服務發現。在服務發現時會根據實例的 host、port、tags(會根據環境 env 進行改變)不斷地新建 instance,然后將 instance 存入緩存池 asyncache,這些 instance 沒有進行清理也就沒有進行內存的釋放。所以這是造成內存泄露的原因。

解決方法

該項目比較早,所以使用的框架比較陳舊,通過升級最新的框架可以解決此問題。

思考總結

首先定義一下什么是內存泄露:

內存泄漏(Memory Leak)是指程序中已動態分配的堆內存由于某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重后果。

常見場景

在 go 的場景中,常見的內存泄露問題有以下幾種:

1. goroutine 導致內存泄露

(1)goroutine 申請過多

問題概述:

goroutine 申請過多,增長速度快于釋放速度,就會導致 goroutine 越來越多。

場景舉例:

一次請求就新建一個 client,業務請求量大時 client 建立過多,來不及釋放。

(2)goroutine 阻塞

① I/O 問題

問題概述:

I/O 連接未設置超時時間,導致 goroutine 一直在等待。

場景舉例:

在請求第三方網絡連接接口時,因網絡問題一直沒有接到返回結果,如果沒有設置超時時間,則代碼會一直阻塞。

② 互斥鎖未釋放

問題概述:

goroutine 無法獲取到鎖資源,導致 goroutine 阻塞。

場景舉例:

假設有一個共享變量,goroutineA 對共享變量加鎖但未釋放,導致其他 goroutineB、goroutineC、...、goroutineN 都無法獲取到鎖資源,導致其他 goroutine 發生阻塞。

③ waitgroup 使用不當

問題概述:

waitgroup 的 Add、Done 和 wait 數量不匹配,會導致 wait 一直在等待。

場景舉例:

WaitGroup 可以理解為一個 goroutine 管理者。他需要知道有多少個 goroutine 在給他干活,并且在干完的時候需要通知他干完了,否則他就會一直等,直到所有的小弟的活都干完為止。我們加上 WaitGroup 之后,程序會進行等待,直到它收到足夠數量的 Done()信號為止。假設 waitgroup Add(2), Done(1),那么此時就剩余一個任務未完成,于是 waitgroup 會一直等待。詳細介紹可以看 Goroutine 退出機制 中的 waitgroup 章節。

2. select 阻塞

問題概述:

使用 select 但 case 未覆蓋全面,導致沒有 case 就緒,最終 goroutine 阻塞。

場景舉例:

通常發生在 select 的 case 覆蓋不全,同時又沒有 default 的時候,會產生阻塞。示例代碼如下:

func main() { ch1 := make(chan int) ch2 := make(chan int) ch3 := make(chan int) go Getdata("https://www.baidu.com",ch1) go Getdata("https://www.baidu.com",ch2) go Getdata("https://www.baidu.com",ch3) select{ case v:=<- ch1: fmt.Println(v) case v:=<- ch2: fmt.Println(v) }}3. channel 阻塞

問題概述:

寫阻塞無緩沖 channel 的阻塞通常是寫操作因為沒有讀而阻塞有緩沖的 channel 因為緩沖區滿了,寫操作阻塞讀阻塞期待從 channel 讀數據,結果沒有 goroutine 往進寫

場景舉例:

上面三種原因的代碼 bug 都會導致 channel 阻塞,這里提供幾個生產環境發生的真實的 channel 阻塞的例子:

lark_cipher 庫機器故障總結Cipher Goroutine 泄漏分析4. 定時器使用不當

(1)time.after()使用不當

問題概述:

默認的 time.After()是會有內存泄漏問題的,因為每次 time.After(duratiuon x)會產生 NewTimer(),在 duration x 到期之前,新創建的 timer 不會被 GC,到期之后才會 GC。

那么隨著時間推移,尤其是 duration x 很大的話,會產生內存泄漏的問題。

場景舉例:

func main() { ch := make(chan string, 100) go func() { for { ch <- "continue" } }() for { select { case <-ch: case <-time.After(time.Minute * 3): } }}

(2)time.ticker 未 stop

問題概述:

使用 time.Ticker 需要手動調用 stop 方法,否則將會造成永久性內存泄漏。

場景舉例:

func main(){ ticker := time.NewTicker(5 * time.Second) go func(ticker *time.Ticker) { for range ticker.C { fmt.Println("Ticker1....") } fmt.Println("Ticker1 Stop") }(ticker) time.Sleep(20* time.Second) //ticker.Stop()}

建議:總是建議在 for 之外初始化一個定時器,并且 for 結束時手工 stop 一下定時器。

5. slice 引起內存泄露

問題概述:

兩個 slice 共享地址,其中一個為全局變量,另一個也無法被 gc;append slice 后一直使用,未進行清理。

場景舉例:

直接上代碼,采用此方式,b 數組是不會被 gc 的。var a []intfunc test(b []int) { a = b[:3] return}

2. 在遇到的其他坑里提到的 kitc 的服務發現代碼就是這個問題的示例。

排查思路總結

今后遇到 golang 內存泄漏問題可以按照以下幾步進行排查解決:

觀察服務器實例,查看內存使用情況,確定內存泄漏問題;可以在 tce 平臺上的【實例列表】處直接點擊;也可以在 ms 平臺上的【運行時監控】進行查看;

2. 判斷 goroutine 問題;

這里可以使用 1 中提到的監控來觀察 goroutine 數量,也可以使用 pprof 進行采樣判斷,判斷 goroutine 數量是否出現了異常增長。

3. 判斷代碼問題;

利用 pprof,通過函數名稱定位具體代碼行數,可以通過 pprof 的 graph、source 等手段去定位;排查整個調用鏈是否出現了上述場景中的問題,如 select 阻塞、channel 阻塞、slice 使用不當等問題,優先考慮自身代碼邏輯問題,其次考慮框架是否存在不合理地方;

4. 解決對應問題并在測試環境中觀察,通過后上線進行觀察;

推薦的排查工具pprof: 是 Go 語言中分析程序運行性能的工具,它能提供各種性能數據包括 cpu、heap、goroutine 等等,可以通過報告生成、Web 可視化界面、交互式終端 三種方式來使用 pprofNemo:基于 pprof 的封裝,采樣單個進程ByteDog:在 pprof 的基礎上提供了更多指標,采樣整個容器/物理機Lidar:基于 ByteDog 的采樣結果分類展示(目前是平臺方更推薦的工具,相較于 nemo 來說)睿智的 oncall 小助手:kite 大佬研究的排查問題小工具,使用起來很方便,在群里 at 機器人,輸入 podName 即可加入我們

飛書是字節跳動旗下先進企業協作與管理平臺,圍繞目標、信息與人三個維度全方位助力組織升級。一站式整合即時溝通、日歷、音視頻會議、文檔、云盤等辦公協作套件,讓組織和個人工作更高效更愉悅。飛書目前已服務包括互聯網、信息技術、制造、建筑地產、教育、媒體在內等眾多領域的先進企業。我們是飛書的Lark Core Services 團隊,負責飛書核心的 IM 領域能力,包括消息、群組、用戶資料、開放能力等等。期待您的加入~

社招鏈接:

https://job.toutiao.com/s/Ne1ovPK

校招鏈接(暑期實習)

https://jobs.toutiao.com/s/NJ3oxsp

拓展知識:

主站蜘蛛池模板: 亚洲成a v人片在线观看| 国产精品特级毛片一区二区三区 | 亚洲图婷婷久久久久久一区| 高h纯肉无码视频在线观看 | 亚洲综合激情另类小说区| 久久人人97超碰a片精品| 国产自国产自愉自愉免费24区| 久久男人av资源网站无码| 欧美伊人色综合久久天天 | 成人国产三级在线观看| 精品一区二区三区在线成人| 无码一区二区三区中文字幕| 老司机香蕉久久久久久| 欧美日韩成人在线一区二区| 欧美精品中文字幕亚洲专区| 日韩综合一区二区在线观看| 99久久国产成人免费网站| 一区二区在线播放日本| 亚洲午夜福利在线观看| 欧美日韩精品一区二区三区在线| 日韩 欧美 动漫 国产 制服| 免费人成在线观看视频高潮| 国产md视频一区二区三区| av电影在线看一区二区| 久久成人麻豆午夜电影| 天堂网在线.www天堂在线资源| 无码少妇一区二区浪潮av| 成人欧美一区二区三区1314| 亚洲色大成网站在线| 少妇高潮喷潮久久久影院| 99re热这里有精品首页| 亚洲日本一区二区三区在线| 国产日产欧产精品网站| 精品成人一区二区三区四区| 亚洲国产长腿丝袜av天堂| 亚洲精品精华液一区| 夜晚成人18禁区导航网站| 乱码午夜-极国产极内射| 成在人线av无码免费高潮水 | 亚洲综合av一区二区三区 | 国产传媒麻豆剧精品av|