Golang Go语言中 http 内存泄漏的问题

Golang Go语言中 http 内存泄漏的问题

代码

package main

import (
	"crypto/tls"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
	"time"
)

func panicIfNotNil(e error) {
	if e != nil {
		panic(e)
	}
}

func loop() {
	ex, err := os.Executable()
	panicIfNotNil(err)

	ex = path.Dir(ex)
	for {
		now := time.Now()
		tr := &http.Transport{
			TLSClientConfig:   &tls.Config{InsecureSkipVerify: true},
			DisableKeepAlives: true, // 可以关闭链接
		}
		req, err := http.NewRequest("GET", "https://httpbin.org/ip", nil)
		panicIfNotNil(err)
		client := &http.Client{
			Timeout:   time.Second * 8,
			Transport: tr,
		}

		resp, err := client.Do(req)

		if resp != nil {
			bodyByteArr, err := ioutil.ReadAll(resp.Body)
			panicIfNotNil(err)

			log.Printf("status_code:%d, body:%s, cost:%v", resp.StatusCode, string(bodyByteArr), time.Since(now))
			defer resp.Body.Close()
		}
		if err != nil && strings.Index(err.Error(), "Client.Timeout exceeded while awaiting headers") > -1 {
			continue
		}
		panicIfNotNil(err)

		time.Sleep(time.Second * 3)

	}
}

func main() {
	loop()
}

一开始只有 4-m 的样子,过了两分钟就到 10M 左右了,这是为什么呀,我可能确保链接时关闭了的 求大佬指点下


更多关于Golang Go语言中 http 内存泄漏的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

22 回复

更多关于Golang Go语言中 http 内存泄漏的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


defer resp.Body.Close() 只会在 func 推出后执行,你这里一直在死循环,函数不推出,resp.Body.Close() 永远不会执行。
你这里直接把 defer 去掉应该就好了。

for 循环里面 defer,楼上正解

去掉了 defer 内存也在涨

去掉了也在涨呀

[vagrant@localhost simple]$ go version
go version go1.15.3 linux/amd64

emmmm,看上去应该没有其他泄露的地方了,你 keepAlive 关了,应该不用手动 CloseIdleConnections 了。
建议你再观察观察,因为你这个是一直在循环,你多观察一会,它是会一直增长,还是会停留在一定程度?
如果涨到一定程度就不涨了,那可能就跟 go 的垃圾回收机制有关了。

现在已经 11m 了

<br>Every 2.0s: ~/soft/pstat --name="simple" --exclude="pstat" Sat Mar 6 22:57:49 2021<br><br>name count mem open_files net_connections<br>---- ----- --- ---------- ---------------<br>simple 1 11.03m 20 0<br><br>

难道大佬们都是全局 client 的吗?

我还是直接把 Transport 中注释贴出来吧,你看看你创建了多少个,正常应该 reused 的

// Transport is an implementation of RoundTripper that supports HTTP,
// HTTPS, and HTTP proxies (for either HTTP or HTTPS with CONNECT).
//
// By default, Transport caches connections for future re-use.
// This may leave many open connections when accessing many hosts.
// This behavior can be managed using Transport’s CloseIdleConnections method
// and the MaxIdleConnsPerHost and DisableKeepAlives fields.
//
// Transports should be reused instead of created as needed.
// Transports are safe for concurrent use by multiple goroutines.
//
// A Transport is a low-level primitive for making HTTP and HTTPS requests.
// For high-level functionality, such as cookies and redirects, see Client.




<br>[vagrantlocalhost simple]$ sudo lsof -p 9575 -a -i 4 <br>COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME<br>simple 9575 vagrant 6u IPv4 6958222 0t0 TCP localhost.localdomain:38460-&gt;<a target="_blank" href="http://ec2-34-231-30-52.compute-1.amazonaws.com" rel="nofollow noopener">ec2-34-231-30-52.compute-1.amazonaws.com</a>:https (ESTABLISHED)<br>

只有一个

现在好像卡在 11.39 不动了

<br>Every 2.0s: ~/soft/pstat --name="simple" --exclude="pstat" Sat Mar 6 23:50:46 2021<br><br>name count mem open_files net_connections<br>---- ----- --- ---------- ---------------<br>simple 1 11.39m 20 0<br><br>

明天在看一下

我之前回复弄错了,没有仔细看代码。我这边是在 11M 到 12M 波动,应该如 7 楼所说。另外建议安装代码检查工具,可以提示那个 defer 以及 resp.Body.Close()的返回值的检查。

pprof 打开观察下就好了 何必在这里猜
代码看起来没啥问题 defer 的问题前面也有人说了

你再测久一些试试看,12M 还太少了。
比如 10 个小时后到了 100M,那才是真的漏了。

Go 1.16 之前,不用的内存还给操作系统的时机都比较延后,你也可以更新版本后试试: https://github.com/golang/go/issues/42330

Go 不是带 GC 的吗?没触发 GC ?

go 1.15 版本的内存回收机制导致的,升级至 1.16 版本试试吧
另外 defer 的问题,每次 new http.Client 的方式也是错的

是这样的, 只要你用了 go 的标准 http 库,内存就会一直多 5m 左右,应该有很多初始化的上下文, 估计要看源码才知道

GC 了也不会把内存还给操作系统,1.16 之前尤其如此

是长链接问题,你看下源码就知道了, 外婆卖瓜下 github.com/dollarkillerx/urllib 这个库有效的解决了这个问题

开一夜试试。我感觉问题不大

在Golang(Go语言)中处理HTTP请求时,内存泄漏通常不是由语言本身直接引起的,而是由于开发者在编写代码时未能正确管理资源所导致的。以下是一些可能导致HTTP内存泄漏的常见原因及解决方案:

  1. 未关闭响应体: 在发起HTTP请求后,如果忘记关闭响应体(resp.Body.Close()),则会导致连接和内存无法被释放。确保在读取完响应数据后关闭响应体。

  2. 全局变量或闭包引用: 如果HTTP处理器函数(handler)中使用了全局变量或闭包引用了外部变量,而这些变量又持有对HTTP请求或响应的引用,那么这些资源可能无法被垃圾回收。检查并避免不必要的全局变量和闭包引用。

  3. 未释放的资源: 在处理HTTP请求时,可能会创建一些临时资源(如文件、数据库连接等)。确保在请求处理完毕后正确释放这些资源。

  4. 并发问题: 在高并发场景下,如果HTTP服务器没有正确管理goroutine的生命周期,可能会导致资源泄露。使用同步原语(如sync.WaitGroup)来确保所有goroutine都已正确退出。

为了检测和修复内存泄漏,可以使用Go的工具链,如pprof(性能分析工具)来监控和分析程序的内存使用情况。通过定期运行内存分析,可以发现并解决潜在的内存泄漏问题。

总之,避免HTTP内存泄漏的关键在于正确管理资源,包括响应体的关闭、避免不必要的全局变量和闭包引用,以及确保并发代码的正确性。

回到顶部