V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
simple2025
0D

golang http 内存泄漏的问题

  •  1
     
  •   simple2025 · Mar 6, 2021 · 4062 views
    This topic created in 1879 days ago, the information mentioned may be changed or developed.

    代码

    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 左右了,这是为什么呀,我可能确保链接时关闭了的 求大佬指点下

    Supplement 1  ·  Mar 6, 2021
    现在内存变成了 ·10.56m·, 感觉还是有内存泄漏


    golang 的版本

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

    系统 centos7
    Supplement 2  ·  Mar 7, 2021
    pprof 了一下

    ```
    Showing nodes accounting for 2097.87kB, 100% of 2097.87kB total
    Showing top 10 nodes out of 14
    flat flat% sum% cum cum%
    1195.29kB 56.98% 56.98% 1195.29kB 56.98% compress/flate.(*compressor).init
    902.59kB 43.02% 100% 2097.87kB 100% compress/flate.NewWriter
    0 0% 100% 2097.87kB 100% compress/gzip.(*Writer).Write
    0 0% 100% 2097.87kB 100% net/http.(*ServeMux).ServeHTTP
    0 0% 100% 2097.87kB 100% net/http.(*conn).serve
    0 0% 100% 2097.87kB 100% net/http.HandlerFunc.ServeHTTP
    0 0% 100% 2097.87kB 100% net/http.serverHandler.ServeHTTP
    0 0% 100% 2097.87kB 100% net/http/pprof.Index
    0 0% 100% 2097.87kB 100% net/http/pprof.handler.ServeHTTP
    0 0% 100% 2097.87kB 100% runtime/pprof.(*Profile).WriteTo
    ```
    Supplement 3  ·  Mar 8, 2021
    我现在也感觉问题,几分钟才增加 200K,不管了,我是菜逼,管那么多干嘛呢,学就是了
    21 replies    2021-03-08 16:25:27 +08:00
    jworg
        1
    jworg  
       Mar 6, 2021
    jinliming2
        2
    jinliming2  
       Mar 6, 2021   ❤️ 1
    defer resp.Body.Close() 只会在 func 推出后执行,你这里一直在死循环,函数不推出,resp.Body.Close() 永远不会执行。
    你这里直接把 defer 去掉应该就好了。
    Mohanson
        3
    Mohanson  
       Mar 6, 2021 via Android   ❤️ 1
    for 循环里面 defer,楼上正解
    simple2025
        4
    simple2025  
    OP
       Mar 6, 2021
    @jinliming2 去掉了 defer 内存也在涨
    simple2025
        5
    simple2025  
    OP
       Mar 6, 2021
    @Mohanson 去掉了也在涨呀
    simple2025
        6
    simple2025  
    OP
       Mar 6, 2021
    [vagrant@localhost simple]$ go version
    go version go1.15.3 linux/amd64
    jinliming2
        7
    jinliming2  
       Mar 6, 2021
    emmmm,看上去应该没有其他泄露的地方了,你 keepAlive 关了,应该不用手动 CloseIdleConnections 了。
    建议你再观察观察,因为你这个是一直在循环,你多观察一会,它是会一直增长,还是会停留在一定程度?
    如果涨到一定程度就不涨了,那可能就跟 go 的垃圾回收机制有关了。
    simple2025
        8
    simple2025  
    OP
       Mar 6, 2021
    @jinliming2 现在已经 11m 了

    ```
    Every 2.0s: ~/soft/pstat --name="simple" --exclude="pstat" Sat Mar 6 22:57:49 2021

    name count mem open_files net_connections
    ---- ----- --- ---------- ---------------
    simple 1 11.03m 20 0

    ```
    simple2025
        9
    simple2025  
    OP
       Mar 6, 2021
    @jinliming2 难道大佬们都是全局 client 的吗?
    jworg
        10
    jworg  
       Mar 6, 2021
    我还是直接把 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.
    simple2025
        11
    simple2025  
    OP
       Mar 6, 2021
    @jworg


    ```
    [vagrant@localhost simple]$ sudo lsof -p 9575 -a -i 4
    COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
    simple 9575 vagrant 6u IPv4 6958222 0t0 TCP localhost.localdomain:38460->ec2-34-231-30-52.compute-1.amazonaws.com:https (ESTABLISHED)
    ```

    只有一个
    simple2025
        12
    simple2025  
    OP
       Mar 6, 2021
    现在好像卡在 11.39 不动了

    ```
    Every 2.0s: ~/soft/pstat --name="simple" --exclude="pstat" Sat Mar 6 23:50:46 2021

    name count mem open_files net_connections
    ---- ----- --- ---------- ---------------
    simple 1 11.39m 20 0

    ```

    明天在看一下
    jworg
        13
    jworg  
       Mar 6, 2021
    @chenqh 我之前回复弄错了,没有仔细看代码。我这边是在 11M 到 12M 波动,应该如 7 楼所说。另外建议安装代码检查工具,可以提示那个 defer 以及 resp.Body.Close()的返回值的检查。
    Immortal
        14
    Immortal  
       Mar 7, 2021
    pprof 打开观察下就好了 何必在这里猜
    代码看起来没啥问题 defer 的问题前面也有人说了
    nanmu42
        15
    nanmu42  
       Mar 8, 2021 via Android
    你再测久一些试试看,12M 还太少了。
    比如 10 个小时后到了 100M,那才是真的漏了。

    Go 1.16 之前,不用的内存还给操作系统的时机都比较延后,你也可以更新版本后试试: https://github.com/golang/go/issues/42330
    binbinyouliiii
        16
    binbinyouliiii  
       Mar 8, 2021
    Go 不是带 GC 的吗?没触发 GC ?
    xkeyideal
        17
    xkeyideal  
       Mar 8, 2021   ❤️ 1
    go 1.15 版本的内存回收机制导致的,升级至 1.16 版本试试吧
    另外 defer 的问题,每次 new http.Client 的方式也是错的
    jitongxi
        18
    jitongxi  
       Mar 8, 2021
    是这样的, 只要你用了 go 的标准 http 库,内存就会一直多 5m 左右,应该有很多初始化的上下文, 估计要看源码才知道
    cheng6563
        19
    cheng6563  
       Mar 8, 2021
    @binbinyouliiii GC 了也不会把内存还给操作系统,1.16 之前尤其如此
    py88pQ2hZ7PJw0v4
        20
    py88pQ2hZ7PJw0v4  
       Mar 8, 2021
    是长链接问题,你看下源码就知道了, 外婆卖瓜下 github.com/dollarkillerx/urllib 这个库有效的解决了这个问题
    xx6412223
        21
    xx6412223  
       Mar 8, 2021
    开一夜试试。我感觉问题不大
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5192 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 70ms · UTC 09:37 · PVG 17:37 · LAX 02:37 · JFK 05:37
    ♥ Do have faith in what you're doing.