- 使用pprof工具:
- 启用pprof:在代码中导入
net/http/pprof
包,并添加如下代码启动pprof服务:
import (
"net/http"
_ "net/http/pprof"
)
func main() {
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
// 其他业务代码
}
- 分析内存:通过浏览器访问
http://localhost:6060/debug/pprof/heap
,可以获取堆内存的分析信息,查找是否有异常增长的对象,特别是与Goroutine相关的对象。也可以使用go tool pprof
命令来分析获取到的内存快照文件,例如go tool pprof http://localhost:6060/debug/pprof/heap
,然后使用top
、list
等命令查看内存使用情况。
- 监控系统指标:
- 使用Prometheus和Grafana:可以使用
prometheus/client_golang
库在Go代码中暴露自定义指标,如Goroutine数量等。例如:
package main
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"log"
"net/http"
)
var goroutineCount = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "goroutine_count",
Help: "Number of active goroutines",
})
func init() {
prometheus.MustRegister(goroutineCount)
}
func main() {
go func() {
for {
goroutineCount.Set(float64(runtime.NumGoroutine()))
time.Sleep(1 * time.Second)
}
}()
http.Handle("/metrics", promhttp.Handler())
log.Fatal(http.ListenAndServe(":8080", nil))
}
- 然后配置Prometheus采集该指标,再通过Grafana展示,观察Goroutine数量是否持续增长且无合理业务原因。
- 代码审查:
- 查找未关闭的通道:在Goroutine中如果有向未关闭通道发送数据的操作,而没有对应的接收方,可能会导致Goroutine阻塞并无法结束,从而造成内存泄漏。例如:
func main() {
ch := make(chan int)
go func() {
ch <- 1
}()
// 这里没有接收ch数据的操作,该Goroutine会一直阻塞
}
- 查找未释放的资源:比如打开的文件、数据库连接等资源在Goroutine结束时没有正确关闭,可能会导致资源泄漏,进而影响内存。例如:
func main() {
go func() {
file, err := os.Open("test.txt")
if err!= nil {
return
}
// 这里没有关闭file,若Goroutine结束,可能导致资源泄漏
}()
}
- 使用race detector:
- 启用race detector:在编译运行代码时添加
-race
标志,例如go run -race main.go
或go build -race main.go
。它可以检测数据竞争问题,有些数据竞争可能与Goroutine内存泄漏相关联,比如由于数据竞争导致Goroutine异常阻塞等情况。如果检测到数据竞争,Go会输出详细的竞争信息,帮助定位问题代码。