现在使用golang的项目越来越多,但是当golang发生内存泄露或cpu占用过高时,怎么定位呢?其实很简单,按如下所述步骤操作即可:

简明步骤:

1、import这个pprof包,然后开启一个端口来监听http请求,注意设置handle为nil。

代码如下:

import _ "net/http/pprof"
go func() {
   mux := http.NewServeMux()
   mux.HandleFunc("/go", func(w http.ResponseWriter, r *http.Request) {
      num := strconv.FormatInt(int64(runtime.NumGoroutine()), 10)
      w.Write([]byte(num))
   })
   http.ListenAndServe("127.0.0.1:6061", mux)
   http.ListenAndServe("127.0.0.1:6060", nil)
}()

解释下代码:开启6061端口可以查看golang当前的routine数,查看是否是routine泄露, 开启6060端口可以查看pprof相关信息

2、使用go命令调用pprof工具来查看内存或cpu状态

go tool pprof http://localhost:6060/debug/pprof/heap
go tool pprof http://localhost:6060/debug/pprof/profile

可以看到如下简单明了的信息,cum是cumulative,意思就是累积量,即累计使用了这么4g左右的内存,cum%就是占所有使用的百分比。根据这些信息,可以看到相应的代码行数,就能快速定位问题了。

=============== 实践:定位内存泄露 =================

问题与现象:线上的程序在部署一周后,使用内存上升了几倍,没有释放的迹象

解决此问题的思路:

1、review代码,看下是否是代码什么地方没有释放。但由于期间更改的代码很多,所以review代码的效率很低,解决问题的速度不够快。(此方法对于代码量修改量小而言,效率是比较高的)

2、使用工具来查看golang的内存状况,从占用内存有异常的相关代码行开始查起。(此方法相当于发烧了用体温计测,测了就知道问题出哪儿了,不过要借助工具)

用方法2测量,结果如下:

go tool pprof http://localhost:6060/debug/pprof/heap
(pprof) top20
Showing nodes accounting for 8547.30MB, 99.65% of 8577.40MB total
Dropped 30 nodes (cum <= 42.89MB)
      flat  flat%   sum%        cum   cum%
 4364.09MB 50.88% 50.88%  4364.09MB 50.88%  0000000000769fba /vreader.(*tClassicVideoReader).Read 
 3634.66MB 42.37% 93.25%  3634.66MB 42.37%  00000000005485fc bytes.makeSlice /usr/local/go/src/bytes/buffer.go:230
  340.03MB  3.96% 97.22%   340.03MB  3.96%  00000000004892a9 time.NewTimer /usr/local/go/src/time/sleep.go:86
  208.52MB  2.43% 99.65%   208.52MB  2.43%  00000000004892db time.NewTimer /usr/local/go/src/time/sleep.go:89
         0     0% 99.65%  3634.66MB 42.37%  0000000000547fb0 bytes.(*Buffer).grow /usr/local/go/src/bytes/buffer.go:144
         0     0% 99.65%  3629.34MB 42.31%  0000000000548159 bytes.(*Buffer).Grow /usr/local/go/src/bytes/buffer.go:163
         0     0% 99.65%  3629.34MB 42.31%  00000000005aca42 io/ioutil.readAll /usr/local/go/src/io/ioutil/ioutil.go:34
         0     0% 99.65%  3629.34MB 42.31%  00000000005acc54 io/ioutil.ReadFile /usr/local/go/src/io/ioutil/ioutil.go:73
         0     0% 99.65%  4364.09MB 50.88%  000000000076ab94 service/vreader.(*tMVSVideoReader).Read 
         0     0% 99.65%   548.54MB  6.40%  00000000007c4ad1 service.(*TFixedPool).inner_deal_pakeage 
         0     0% 99.65%   548.54MB  6.40%  00000000007c5087 service.inner_worker_send_recv 
         0     0% 99.65%   548.54MB  6.40%  00000000007c6a27 service.inner_worker_send_recv_with_timeout 
         0     0% 99.65%  3629.34MB 42.31%  00000000007c9c19 service.update_worker_face_feature 
         0     0% 99.65%   546.54MB  6.37%  00000000007ce9af service.(*TFixedPool).add_worker.func1 
         0     0% 99.65%  4364.09MB 50.88%  00000000007f3e38 apps/huaruncheng.hrcAiRoutineOneTry 

分析下:

1、占用内存4G左右的classic_reader看了代码,确认是缓存视频用到的内存,正常现象。

2、bytes.makeSlice占用了3G内存,看了代码发现是读文件同步特征值时创建buf产生的内存,正常现象。

3、time.NewTimer占用500M,根据关联查代码,计时器不应该用这么多内存,这里是有异常的。

走读了这部分涉及计时器的代码后发现是因为有个计时器在某些场景下设置了很大的触发时间(有的场景需要比较大的触发时间),但是在正确回包后又没有及时stop掉,所以就泄露了

定位cpu占用过高的方法也是一样的,就不展开了

优质内容筛选与推荐>>
1、JPush极光推送Java服务器端API
2、Screen会话命令 Linux
3、《银河英雄传说》杨威利经典语录1(田中芳树最经典的作品,我顶级拥护!有志同道合的一定要来给我留言!)
4、POJ 2546 Circular Area 求两圆的重合面积
5、003 对象