1. 在main包中,选择在一个单独文件中加上pprof包引用并设置条件编译,这样不会与develop正式版本代码产生任何耦合。

// +build debug

package main

import (
	"net/http"
	_ "net/http/pprof"
)

func init() {
	go http.ListenAndServe(":18080", nil)
}

2. 编译

 go build -tags debug .

3. 在目标机中运行刚编译出来的debug版本程序。本例中ip为随意编写的。

4. 执行go tool pprof -inuse_space  http://192.168.20.5:18080/debug/pprof/heap进入交互模式:

[user@LAPTOP-LCP testtool]$ go tool pprof -inuse_space http://192.168.20.5:18080/debug/pprof/heap
Fetching profile over HTTP from http://192.168.20.5:18080/debug/pprof/heap
Saved profile in C:\Users\Noname\pprof\pprof.testtool.alloc_objects.alloc_space.inuse_objects.inuse_space.006.p
b.gz
File: testtool
Type: inuse_space
Time: Aug 16, 2022 at 2:24pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top 10
Showing nodes accounting for 27831.78kB, 100% of 27831.78kB total
Showing top 10 nodes out of 35
      flat  flat%   sum%        cum   cum%
11787.50kB 42.35% 42.35% 11787.50kB 42.35%  runtime.allocm
 9382.95kB 33.71% 76.07%  9382.95kB 33.71%  io.ReadAll
 2561.02kB  9.20% 85.27%  2561.02kB  9.20%  runtime.malg
 1536.21kB  5.52% 90.79%  1536.21kB  5.52%  github.com/aws/aws-sdk-go/aws/endpoints.init
     514kB  1.85% 92.63%      514kB  1.85%  bufio.NewReaderSize
     514kB  1.85% 94.48%      514kB  1.85%  bufio.NewWriterSize
  512.08kB  1.84% 96.32%   512.08kB  1.84%  net/http.newTransferWriter
  512.02kB  1.84% 98.16%   512.02kB  1.84%  runtime.gcBgMarkWorker
     512kB  1.84%   100%      512kB  1.84%  runtime.doaddtimer
         0     0%   100%  9382.95kB 33.71%  io/ioutil.ReadAll

(pprof)
(pprof)

交互界面使用top命令查看内存分配最多的函数。

如果本机中有源代码,可以使用list命令查看源代码中哪一行分配的函数最多。

(pprof)
(pprof) list io.ReadAll
Total: 27.18MB
ROUTINE ======================== io.ReadAll in C:\Program Files\Go\src\io\io.go
    9.16MB     9.16MB (flat, cum) 33.71% of Total
         .          .    626:func ReadAll(r Reader) ([]byte, error) {
         .          .    627:   b := make([]byte, 0, 512)
         .          .    628:   for {
         .          .    629:           if len(b) == cap(b) {
         .          .    630:                   // Add more capacity (let append pick how much).
    9.16MB     9.16MB    631:                   b = append(b, 0)[:len(b)]
         .          .    632:           }
         .          .    633:           n, err := r.Read(b[len(b):cap(b)])
         .          .    634:           b = b[:len(b)+n]
         .          .    635:           if err != nil {
         .          .    636:                   if err == EOF {
(pprof)
(pprof)
 

5. 也可以使用以下命令直接导出调用图,非常直观(需要安装Graphviz,详询Download | Graphviz)

go tool pprof -inuse_space -png http://192.168.20.5:18080/debug/pprof/heap > heap.png

 可以在图片中直观地看到,调用过程中每个函数的内存使用占比。

出现内存泄漏时,如果只查看一次很难确定真正出现内存泄露的代码位置,可以在程序运行一段时间后,程序出现内存泄露已经很明显后,再次查看内存占用分析图,找到两张图中内存占用比例相差最大的函数。