• 记一次golang内存泄露


    记一次golang内存泄露

    最近在QA环境上验证功能时,发现机器特别卡,查看系统内存,发现可用(available)内存仅剩200多M,通过对进程耗用内存进行排序,发现有一个名为application-manager的容器服务的内存占用达到700多M,该服务使用Gin框架对外提供操作k8s资源的简单功能,解析客户端请求,并将结果返回给客户端。由于是测试环境,访问量极少,但内存一直只增不减,从最初的10M,一直增加到700多M,最终由于OOM而被重启(Pod)。

    最初使用go pprof来尝试定位是否代码中存在如未释放的全局变量或存在goroutine泄露。初步定位后发现并没有goroutine泄露。/debug/pprof/allocs导出的svg文件参见这里

    # go tool pprof http://127.0.0.1/debug/pprof/allocs
    Fetching profile over HTTP from http://oms.qa.internal.hsmob.com/debug/pprof/allocs
    Saved profile in C:Usersliuchpprofpprof.application-manager.alloc_objects.alloc_space.inuse_objects.inuse_space.005.pb.gz
    File: application-manager
    Type: alloc_space
    Time: May 24, 2021 at 9:21am (CST)
    Entering interactive mode (type "help" for commands, "o" for options)
    (pprof) web
    

    内存占用图中唯一一个与自己的功能有关的代码就是setK8sEvent,至此可以初步怀疑问题所在。但问题点并不明确。另外在图中可以看到占用内存最多的函数是goLookupIpCNAMEOrder,该函数与域名查找有关,可能是建立链接前的步骤。

    进入该容器(application-manager),查看底层链接情况,可以看到在该容器中建立了9492条TCP链接。至此已经找到问题根因,内存泄露的原因是没有及时关闭TCP,导致创建了大量socket,占用大量内存。

    # ss -s
    Total: 10215 (kernel 15097)
    TCP:   9492 (estab 7953, closed 1537, orphaned 0, synrecv 0, timewait 1106/0), ports 0
    
    Transport Total     IP        IPv6
    *         15097     -         -
    RAW       0         0         0
    UDP       0         0         0
    TCP       7955      7952      3
    INET      7955      7952      3
    FRAG      0         0         0
    

    在容器中使用ss -ntp命令进一步查看是创建了到哪些服务链接。最终发现创建了大量到elasticsearch 9200端口的链接,结合前面提到的setK8sEvent函数(该函数会将k8s事件发送给elasticsearch),基本可以确定是在读取es的数据之后,忘了执行resp.Body.Close()操作。

    后记

    代码中的确存在没有执行resp.Body.Close()的操作,修复之后发现内存占用正常。

    总结

    • 如果golang程序发现内存泄露,可以首先检查socket泄露。
  • 相关阅读:
    tomcat 乱码问题
    mongo获取lbs数据
    sping mvc+uploadify 上传文件大小控制3部曲
    jstack 查看线程状态
    nginx 限流配置
    查找占用CPU高线程
    redis sentinel无法启动问题
    GC垃圾回收机制
    ASP.NET 生命周期
    Javascript验证手机号码正则表达式
  • 原文地址:https://www.cnblogs.com/charlieroro/p/14803312.html
Copyright © 2020-2023  润新知