Golang --time-After-- 在計時器過期前不會被垃圾回收

最近我在調查 Go 應用程序中內存泄漏的問題,這個問題主要因爲我沒有正確的閱讀文檔。這是一段導致消耗了多個 Gbs 內存的代碼:

func ProcessChannelMessages(ctx context.Context, in <-chan string, idleCounter prometheus.Counter) {
 for {
  start := time.Now()
  select {
  case s, ok := <-in:
   if !ok {
    return
   }
   // handle `s`
  case <-time.After(5 * time.Minute):
   idleCounter.Inc()
  case <-ctx.Done():
   return
  }
 }
}

以下是應用程序的內存指標圖:

在圖中左側,可以看到修復之前的內存消耗,右側是修改後的內存消耗。分析器顯示 <-time.After 是內存泄漏的原因。直到我讀到以下文檔時,我才感到驚訝:

在計時器觸發之前,垃圾收集器不會回收 Timer。

所以 9Gb 的內存被定期垃圾回收變得非常必要了。我們在 channel 中每秒有 60k 個消息,在每個給定時刻分配大約 1800 萬個計時器加上一些不確定數字的計時器等待被垃圾回收。

func ProcessChannelMessages(ctx context.Context, in <-chan string, idleCounter prometheus.Counter) {
 idleDuration := 5 * time.Minute
 idleDelay := time.NewTimer(idleDuration)
 defer idleDelay.Stop()
 for {
  idleDelay.Reset(idleDuration)
  select {
  case s, ok := <-in:
   if !ok {
    return
   }
   // handle `s`
  case <-idleDelay.C:
   idleCounter.Inc()
  case <-ctx.Done():
   return
  }
 }
}

微不足道的重構有助於將內存消耗減少 20 倍,這些都是閱讀文檔就能解決的問題。


via: https://medium.com/@oboturov/golang-time-after-is-not-garbage-collected-4cbc94740082

作者:Artem OBOTUROV[1] 譯者:咔嘰咔嘰 [2] 校對:polaris1119[3]

本文由 GCTT[4] 原創編譯,Go 中文網 [5] 榮譽推出,發佈在 Go 語言中文網公衆號,轉載請聯繫我們授權。

參考資料

[1]

Artem OBOTUROV: https://medium.com/@oboturov

[2]

咔嘰咔嘰: https://github.com/watermelo

[3]

polaris1119: https://github.com/polaris1119

[4]

GCTT: https://github.com/studygolang/GCTT

[5]

Go 中文網: https://studygolang.com/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/HtvJMGWmGnToplaz54pUaw