Member-only story
How Go 1.23 eliminates Timer and Ticker memory leaks?
Before Go 1.23, the developers had to write a few lines at the defer
statement, in order to make sure, possible memory leaks were avoided.
If you cannot see the whole article, click here.

The issue
Before Go 1.23, the GC could not free the allocated memory of time.Timer and time.Ticker, until the time.Timer (used by time.Ticker) instance has not Stopped and drained.
It’s not mentioned at the documentation of time.NewTimer
, see time.NewTimer@1.22, but mentioned only at time.Stop
, see time.Stop@1.22:
To ensure the channel is empty after a call to Stop, check the return value and drain the channel. For example, assuming the program has not received from t.C already:
if !t.Stop() {
<-t.C
}
Practically, the channel close (called by time.Stop
) and channel draining should always be added at defer statement, after the time.NewTimer
or time.NewTicker
, for example:
timer := time.NewTimer(100 * time.Millisecond)
defer func() {
if !timer.Stop() {
<-timer.C
}
}()
This pattern must be used manually, which is easy to forget.
Behind this behavior, there is a Golang design concept: a channel can be garbage collected, if it’s…