這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
在謝大群裡看到有同學在討論time.After
泄漏的問題,就算時間到了也不會釋放,瞬間就驚呆了,忍不住做了實驗,結果發現應該沒有這麼的恐怖的,是有泄漏的風險不過不算是泄漏,先看API的說明:
// After waits for the duration to elapse and then sends the current time// on the returned channel.// It is equivalent to NewTimer(d).C.// The underlying Timer is not recovered by the garbage collector// until the timer fires. If efficiency is a concern, use NewTimer// instead and call Timer.Stop if the timer is no longer needed.func After(d Duration) <-chan Time { return NewTimer(d).C}
提到了一句The underlying Timer is not recovered by the garbage collector
,這句挺嚇人不會被GC回收,不過後面還有條件until the timer fires
,說明fire
後是會被回收的,所謂fire
就是到時間了,寫個例子證明下壓壓驚:
package mainimport "time"func main() { for { <- time.After(10 * time.Nanosecond) }}
顯示記憶體穩定在5.3MB,CPU為161%,肯定被GC回收了的。當然如果放在goroutine也是沒有問題的,一樣會回收:
package mainimport "time"func main() { for i := 0; i < 100; i++ { go func(){ for { <- time.After(10 * time.Nanosecond) } }() } time.Sleep(1 * time.Hour)}
只是資源消耗會多一點,CPU為422%,記憶體佔用6.4MB。因此:
Remark: time.After(d)在d時間之後就會fire
,然後被GC回收,不會造成資源泄漏的。
那麼API所說的If efficieny is a concern, user NewTimer instead and call Timer.Stop
是什麼意思呢?這是因為一般time.After
會在select中使用,如果另外的分支跑得更快,那麼timer是不會立馬釋放的(到期後才會釋放),比如這種:
select { case time.After(3*time.Second): return errTimeout case packet := packetChannel: // process packet.}
如果packet非常多,那麼總是會走到下面的分支,上面的timer不會立刻釋放而是在3秒後才能釋放,和下面代碼一樣:
package mainimport "time"func main() { for { select { case <-time.After(3 * time.Second): default: } }}
這個時候,就相當於會堆積了3秒的timer沒有釋放而已,會不斷的建立和釋放timer,記憶體會穩定在2.8GB,這個當然就不是最好的了,可以主動釋放:
package mainimport "time"func main() { for { t := time.NewTimer(3*time.Second) select { case <- t.C: default: t.Stop() } }}
這樣就不會佔用2.8GB記憶體了,只有5MB左右。因此,總結下這個After的說明:
- GC肯定會回收
time.After
的,就在d之後就回收。一般情況下讓系統自己回收就好了。
- 如果有效率問題,應該使用
Timer
在不需要時主動Stop。大部分時候都不用考慮這個問題的。
交作業。
697 次點擊