Go语言:利用pprof工具排查内存泄漏的示例

本文介绍如何使用Go内置的pprof工具排查内存泄漏问题。通过示例代码展示如何定位由全局变量引起的内存占用过高的问题,并提供详细的步骤指导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Go中的内存泄漏通常是指在运行过程中全局变量所分配的内存越来越多,而没有释放。利用自带的pprof工具可以很方便的排查这类问题。下面的示例为了简化问题,仅仅是在函数调用过程中为两个全局变量分别赋值一个200MB和100MB的切片。

示例代码pprof.go

调用关系:

  • handler1 > func1_1 > func1_2 (创建200MB的切片) > func1_3
  • handler2 > func2_1 > func2_2 > func2_3(创建100MB的切片)
package main

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

var container []byte
var container2 []byte

func func1_3() {
	fmt.Printf("handler1 %d\n", len(container))
}

func func1_2() {
	container = make([]byte, 200*1024*1024)
	for i := 0; i < 200*1024*1024; i++ {
		container[i] = 0
	}

	func1_3()
}

func func1_1() {
	func1_2()
}

func func2_3() {
	container2 = make([]byte, 100*1024*1024)
	for i := 0; i < 100*1024*1024; i++ {
		container2[i] = 0
	}
	fmt.Printf("handler2 %d\n", len(container2))
}

func func2_2() {
	func2_3()
}

func func2_1() {
	func2_2()
}

func handler1(w http.ResponseWriter, r *http.Request) {
	func1_1()
	runtime.GC()
}

func handler2(w http.ResponseWriter, r *http.Request) {
	func2_1()
	runtime.GC()
}

func main() {
	http.HandleFunc("/test1", handler1)
	http.HandleFunc("/test2", handler2)
	http.ListenAndServe(":8888", nil)
}

请求/test1和/test2各一次,等待一段时间直到进程的RSS不再下降:

top命令:

 322m大致等于container,container2两个全局变量的内存占用之和。

执行go tool pprof -inuse_space  http://127.0.0.1:8888/debug/pprof/heap进入交互模式:

结果排序方式:

  1. 首先按函数所申请的内存大小倒排序,
  2. 然后按调用顺序从下到上,
  3. 如果内存大小相同又没有调用关系的则按字符串排序,如func1_1排在func2_1前面。

可以看到,main.func1_2和main.func2_3是申请内存最多的两个函数,这与前面的预期是一致的。另外,虽然func1_2还调用了func1_3但是func1_3不会在这里显示。

在交互模式下,输入list命令 + 函数名称可以进一步看到内存申请在函数中的具体位置。这里必须保证go的源文件在执行go tool pprof命令的机器上能够找得到,否则会报“Error: Could not find file”。

 到这里,结合着代码来看基本上就能找到内存泄漏的点。如果要进一步查看调用关系,可以把上述top命令的输出结果导出为图片:

go tool pprof -inuse_space -png http://127.0.0.1:8888/debug/pprof/heap > heap.png

这条命令需要依赖Graphviz,在Centos上可以直接用yum install来安装:

yum install graphviz

windows需要下载安装文件graphviz-2.38.msi,安装完毕后配置系统环境变量C:\Program Files (x86)\Graphviz2.38\bin

dot -v有返回则安装成功。

[root@dev tutorial]# dot -v
dot - graphviz version 2.26.0 (20091210.2329)

heap.png

相关文章:

《Go语言:利用pprof工具查找goroutine(协程)泄漏的示例》 

《Go语言:利用pprof工具查看CPU占用情况的示例》

### pprof调试信息泄露漏洞概述 #### 影响范围 Kubernetes PPROF内存泄漏漏洞(CVE-2019-11248)影响了 Kubernetes 的 1.18.6 版本之前的版本。此漏洞允许未经授权的用户通过 Kubelet 组件中的 `/debug/pprof` 调试端点获取敏感信息,而这些信息通常仅限于授权人员访问[^1]。 对于 Go 应用程序而言,当开发者引入 `import _ "net/http/pprof"` 后,在默认情况下开启了 HTTP 接口用于性能剖析和故障排查功能。如果应用程序部署在网络环境中且未采取适当的安全措施,则可能暴露出内部状态给外部网络,造成潜在风险[^3]。 #### 漏洞原理 该漏洞的核心问题是由于某些服务启用了 pprof 功能却没有对其进行充分保护所致。具体来说: - **Kubernetes场景下**:Kubelet 默认监听本地地址上的健康检查端口 (healthz port),并且在这个端口中包含了未经身份验证即可访问到的 pprof 数据接口 (`/debug/pprof`) 。这意味着任何能够连接至目标主机的人都能利用这个开放路径来收集有关集群节点运行状况的信息,甚至执行进一步攻击行为。 - **通用Go应用层面**:只要项目中导入了 `"net/http/pprof"` 并绑定了公开可到达的服务端口,就有可能让远程客户端无需认证就能抓取到进程内的各种诊断数据,比如 goroutine 列表、堆栈跟踪等私密资料。 ```go // 示例代码展示了如何不当配置pprof可能会导致安全隐患 package main import ( "log" "net/http" _ "net/http/pprof" // 导入pprof包但不显式注册路由 ) func main() { log.Fatal(http.ListenAndServe(":6060", nil)) } ``` 上述例子中,虽然没有显示定义具体的 URL 映射关系,但由于 `_ "net/http/pprof"` 的存在,默认会在根路径下提供一系列有用的工具页面,这正是隐患所在之处。 #### 修复方法 针对此类问题的有效解决方案包括但不限于以下几个方面: - **限制访问权限** - 只有经过严格的身份验证机制才能接触pport所提供的资源; - 使用防火墙规则或其他网络安全策略阻止来自不可信源的数据流入含有pport接口的应用实例; - **移除不必要的特性** - 如果确实不需要使用 pprof 工具来进行日常运维操作的话,建议彻底删除相关依赖项以及对应的初始化逻辑; - **加密通信渠道** - 当必须保留这项能力时,应当考虑启用 TLS 加密传输层协议以确保即使被窃听也无法轻易解读所交换的内容; - **更新软件版本** - 针对已知受影响的产品线及时跟进官方发布的补丁文件完成升级工作,例如将 Kubernetes 更新到不低于 v1.18.7 或者更高稳定版次。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值