注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
使用 gin 作为文件下载服务器,内存占用突然从几十 M 到了 10G 以上,导致服务被 kill 重启
server.go
1 | |
client.go
1 | |
使用pprof我们可以发现内存占用高达3GB, 即使我主动调用了 GC 这个内存仍未释放

通过查看代码我们可以发现请求已经结束,代码并没有其他地方对[]byte引用,一直追溯到最低层也不见其他引用。
但是结束client进程之后会有一个神奇的发现,结束 client 之后这一块内存就可以被 GC 掉
通过这个现象自然而然的就想到可能是 TCP 链接没有断开,导致这一块内存的引用并没有被释放掉
http 是一个本身是短连接,但是为了复用 TCP 连接所以有了keep-alive,但是对于下载服务来说我们其实不用复用 TCP 连接,只需要在文件下载完毕之后主动关闭这个连接即可,所以我分别在 client 加上了一个 header
1 | |
再次通过pprof查看内存占用发现内存仍未得到释放
通过 rfc 文档,我们可以发现规范并没有规定由谁来关闭链接,Go net/http 希望客户端关闭链接
https://tools.ietf.org/html/rfc2616#page-117
HTTP/1.1 defines the “close” connection option for the sender to signal that the connection will be closed after completion of the response.
gin中不要直接使用c.Data而是使用c.DataFromReadernet/http处理文件注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
使用 gin 作为文件下载服务器,内存占用突然从几十 M 到了 10G 以上,导致服务被 kill 重启
server.go
1 | |
client.go
1 | |
使用pprof我们可以发现内存占用高达3GB, 即使我主动调用了 GC 这个内存仍未释放

通过查看代码我们可以发现请求已经结束,代码并没有其他地方对[]byte引用,一直追溯到最低层也不见其他引用。
但是结束client进程之后会有一个神奇的发现,结束 client 之后这一块内存就可以被 GC 掉
通过这个现象自然而然的就想到可能是 TCP 链接没有断开,导致这一块内存的引用并没有被释放掉
http 是一个本身是短连接,但是为了复用 TCP 连接所以有了keep-alive,但是对于下载服务来说我们其实不用复用 TCP 连接,只需要在文件下载完毕之后主动关闭这个连接即可,所以我分别在 client 加上了一个 header
1 | |
再次通过pprof查看内存占用发现内存仍未得到释放
通过 rfc 文档,我们可以发现规范并没有规定由谁来关闭链接,Go net/http 希望客户端关闭链接
https://tools.ietf.org/html/rfc2616#page-117
HTTP/1.1 defines the “close” connection option for the sender to signal that the connection will be closed after completion of the response.
gin中不要直接使用c.Data而是使用c.DataFromReadernet/http处理文件