You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go中长时间运行的Ticker协程内存泄漏问题咨询

解答你的Go协程内存泄漏问题

Hey there! Let's break down what's happening here and why your fix worked, plus some extra tips to make things more robust.

First, let's clear up a common misconception

Your initial thought that for t := range ticker.C causes memory leak because of re-declaring t each loop isn't right. Go's garbage collector (GC) is smart enough to clean up local variables like t once they're no longer referenced. Each iteration's t is a value-type (time.Time) stored on the stack, and once the loop iteration finishes, if nothing is holding onto it, the GC will free that memory. So that line alone isn't the culprit.

Why did switching to for _ = range ticker.C fix the leak?

The most likely reason is that in your actual update logic (not the simplified fmt.Println in your example), you were accidentally keeping a reference to t somewhere long-lived. For example:

  • Adding t to a global slice/map that never gets cleared
  • Storing t in a field of a struct that stays in memory for the program's lifetime
  • Passing t to a function that retains it indefinitely

By not assigning t at all (using _ to discard the value), you eliminate those accidental references. Without anything holding onto the time values from ticker.C, the GC can clean them up properly, stopping the memory leak.

A quick note on your fmt.Println line

You've got a small bug here: you're using fmt.Println with a format string that has %d, but t is a time.Time value. %d expects an integer, so this won't print correctly. You should use fmt.Printf instead, with a format specifier like %v or %s for time:

fmt.Printf("the update check happened at %v\n", t)

Improving your ticker's lifecycle

Right now, your defer ticker.Stop() will never run because the for range ticker.C loop blocks forever (until the program is killed). While this isn't causing your leak, it's not the most graceful way to handle things. If you ever need to shut down this coroutine cleanly (instead of killing the whole program), you can add a stop channel:

func (s *Server) checkForUpdates(stopChan <-chan struct{}) {
	ticker := time.NewTicker(time.Minute * time.Duration(s.checkTime))
	defer ticker.Stop() // Now this will run when we exit the function

	for {
		select {
		case t := <-ticker.C:
			fmt.Printf("the update check happened at %v\n", t)
			// Do your update logic here—just make sure not to hold onto t unnecessarily!
		case <-stopChan:
			// Received signal to stop, exit the loop
			return
		}
	}
}

To use this, you'd create a channel in your main code and pass it to the coroutine. When you want to shut down, close the channel:

stopChan := make(chan struct{})
go server.checkForUpdates(stopChan)

// When it's time to stop:
close(stopChan)

Wrap-up

Your fix worked because you removed accidental references to the time values from the ticker channel. The loop variable declaration itself wasn't the problem—look closely at your update logic to make sure you're not holding onto t (or any other temporary values) longer than needed. And adding a stop channel will make your coroutine more flexible and clean.

内容的提问来源于stack exchange,提问作者JackR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:36:03