zoukankan      html  css  js  c++  java
  • 记一次golang的内存泄露

    程序功能

    此程序的主要功能是将文件中数据导入到clickhouse数据库中。

    【问题描述】

    服务器内存每隔一段时间会耗尽

    【问题分析】

    由于使用的是go语言开发的,所以采用了业界流行的工具pprof。

    参考URL:https://cizixs.com/2017/09/11/profiling-golang-program/

    工具的使用与思路:
    1)先修改源代码
    2)安装工具观察
    3)根据工具抓取的现象进行分析
    4)修复内存缺陷代码, 再根据分析结果修复内存泄漏的地方
    5)发布代码进行再跟踪分析

    1)修改代码:
    使用这个工具前需要在代码中写几行代码,以便能使用这个工具的来收集数据。

    1 //引用pprof
    2 import "net/http"
    3 import_ "net/http/pprof"
    4
    5 //在主函数中新增端口监控程序
    6 //由于我的代码本来就是守护进程,所以这里采用新开一个监听协程方式,防止阻塞
    7 func main(){
    8 go func(){
    9 http.ListenAndServe("0.0.0.0:80", nil)
    10 }()
    11 //其他代码
    12 ...
    13 }

    经过上面的源代码改造后,重新部署到服务器上,观察内存状况;
    内存仍然可以重新持续消耗内存不释放的现象。

    2)在服务器上安装 golang pprof 程序,进行数据采集。

    安装方法:yum install golang pprof

    3)使用命令对heap进行dump分析,这个工具的好处是dump后可以直接生成pdf或png

    1 [root@centos ~]# go tool pprof /root/clickhouse_runner/clickhouse_mssql_e
    tl http://0.0.0.0:80/debug/pprof/heap
    2 Fetching profile over HTTP from http://0.0.0.0:80/debug/pprof/heap
    3 Saved profile in /root/pprof/pprof.clickhouse_mssql_etl.alloc_objects.all
    oc_space.inuse_objects.inuse_space.012.pb.gz
    4 File: clickhouse_mssql_etl
    5 Type: inuse_space
    6 Time: Feb 5, 2020 at 4:15pm (CST)
    7 Entering interactive mode (type "help" for commands, "o" for options)
    8 (pprof) pdf
    9 Generating report in profile003.pdf
    10 (pprof) quit
    11 [root@centos ~]



    通过上面的heap 来分析,可以很明显的看到代码中主要的内存使用地方在于clickhouse 的驱动中,调用clickhouse的部分在创建内存没有释放(后来仔细分析了下golang的内存gc逻辑是由于gc速度存在滞后现象,而导入程序创建速度又很快,所以才导致gc越来越慢)。

    4)找到内存泄漏的源头,开始修改代码
    修改前源代码:

    1 connect, err := sql.Open("clickhouse", connstring)
    2 if err != nil {
    3 return err
    4 }
    5 load_start := time.Now()
    6 tx, err := connect.Begin()
    7 if err != nil {
    8 log.Println(full_filename, "begin err", err)
    9 return err
    10 }
    11 stmt, err := tx.Prepare("insert ... values....")
    12 if err != nil {
    13 log.Println(full_filename, "preare err", err)
    14 return err
    15 }
    16 _, er := stmt.Exec(...)
    17 if er != nil {
    18 log.Println("err", er)
    19 }
    20 er2 := tx.Commit()
    21 if er2 != nil {
    22 log.Println(db_view, "err", er2)
    23 }
    24 stmt.Close()
    25 connect.Close()

    //通过自己写的代码与clickhouse 驱动代码的分析,总结可以有两种方式来改进内存泄
    漏:
    a.修改clickhouse中的驱动代码,再执行完代码后立即进行重置内存,而不等gc来处理:

    1 func (stmt *stmt) Close() error {
    2 stmt.ch.logf("[stmt] close")
    3 //新增再次回收内存数据
    4 if stmt.ch.block != nil {
    5 stmt.ch.block.Reset()
    6 }
    7 return nil
    8 }

    b. 直接释放stmt的对象,利用gc 的自动回收(考虑后还是采用这个方式更合理些)

    1 stmt.Close()
    2 connect.Close()
    3 //新增直接将stmt,connect对象置nil
    4 //clear mem
    5 stmt = nil
    6 tx = nil
    7 connect = nil

    修改后完整的代码:

    1 connect, err := sql.Open("clickhouse", connstring)
    2 if err != nil {
    3 return err
    4 }
    5 load_start := time.Now()
    6 tx, err := connect.Begin()
    7 if err != nil {
    8 log.Println(full_filename, "begin err", err)
    9 return err
    10 }
    11 stmt, err := tx.Prepare("insert ... values....")
    12 if err != nil {
    13 log.Println(full_filename, "preare err", err)
    14 return err
    15 }
    16 _, er := stmt.Exec(...)
    17 if er != nil {
    18 log.Println("err", er)
    19 }
    20 er2 := tx.Commit()
    21 if er2 != nil {
    22 log.Println(db_view, "err", er2)
    23 }
    24 stmt.Close()
    25 connect.Close()
    26
    27 //***** clear mem for gc ******
    28 stmt = nil
    29 tx = nil
    30 connect = nil
    31 //////////////////////////////////////////////////////////////////////////////////

    5) 发布修改后的代码,进行观察,通过观察发现系统内存可以正常回收与释放

    【结论】

    经过本次golang的调试发生,真正的原因是gc内存释放不够及时,存在滞后性(通过其他服务器观察发现,当压力小的时候,内存是可以正常释放的)。
    所以最佳实践还是,在涉及到golang中使用大对象或者频繁创建内存的时候,要采用将对象设置能obj = nil 的方式,告知gc 我已经确实不再使用该内存块了,以便gc快速的回收,减少迭代gc。
    另外,这种方式是可以应用到如java,c# 等语言身上的,它们都存在类似的问题。

  • 相关阅读:
    git和github入门指南(3.1)
    git和github入门指南(2.2)
    git和github入门指南(2.1)
    git和github入门指南(1)
    webpack入门进阶(3)
    webpack入门进阶(2)
    webpack入门进阶(1)
    vue全家桶(4.3)
    vue全家桶(4.2)
    vue全家桶(4.1)
  • 原文地址:https://www.cnblogs.com/CtripDBA/p/12289939.html
Copyright © 2011-2022 走看看