Go语言调用http.Client.Do方法引发内存泄漏该如何修复
问题根源
内存泄漏的核心原因是每次调用getAPIData函数都重新实例化http.Client:
http.Client本身是线程安全的,设计定位就是全局复用的对象,内部会维护TCP连接池来复用HTTP连接- 每次新建
http.Client都会生成独立的连接池,旧Client对应的连接、缓冲等资源无法被及时回收,长期运行就会出现内存持续上涨的情况
修复方案
核心修改
将http.Client提取为全局实例,初始化一次后所有请求共用即可,不需要每次请求都新建。
可选规范优化
Go 1.16及以上版本中,ioutil.ReadAll已经被废弃,可以替换为标准库io包下的io.ReadAll,该修改不影响内存泄漏问题,属于写法规范优化。
修复后完整代码
package main import ( "fmt" "io" "net/http" "runtime" "time" ) // 全局复用http.Client var client = &http.Client{Timeout: 3 * time.Second} func getAPIData() (string, error) { req, err := http.NewRequest("GET", "https://google.com/", nil) if err != nil { return "", err } req.Header.Add("User-Agent", "Mozilla/5.0") resp, err := client.Do(req) if err != nil { fmt.Println(err.Error()) return "", err } defer resp.Body.Close() bytes, err2 := io.ReadAll(resp.Body) if err2 != nil { fmt.Println(err2.Error()) return "", err2 } return string(bytes), nil } func PrintMemUsage() { var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc)) fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc)) fmt.Printf("\tSys = %v MiB", bToMb(m.Sys)) fmt.Printf("\tNumGC = %v\n", m.NumGC) } func bToMb(b uint64) uint64 { return b / 1024 / 1024 } func main() { for range time.Tick(5 * time.Second) { _, _ = getAPIData() PrintMemUsage() } }
修改后运行可以观察到Alloc内存会在GC后稳定在固定区间,不会持续上涨。
内容的提问来源于stack exchange,提问作者DK2
相关产品推荐
相关产品推荐

