本文介绍如何综合运用性能监控(火焰图等)工具,观测并发现应用程序存在的问题。
模拟Golang应用内存泄露的示例程序
package main
import (
"github.com/pyroscope-io/client/pyroscope"
"log"
"os"
"runtime"
"time"
)
type demo struct {
slice []byte
}
var demos = make([]*demo, 0, 1024)
var demosNormal = make([]*demo, 0, 1024)
var beforeTime time.Time
func main() {
runtime.SetBlockProfileRate(100)
runtime.SetMutexProfileFraction(100)
serverAddress := os.Getenv("PYROSCOPE_SERVER_ADDRESS")
if serverAddress == "" {
serverAddress = "http://localhost:4040"
}
appName := os.Getenv("PYROSCOPE_APPLICATION_NAME")
if appName == "" {
appName = "go-leak-app"
}
_, err := pyroscope.Start(pyroscope.Config{
ApplicationName: appName,
ServerAddress: serverAddress,
AuthToken: os.Getenv("PYROSCOPE_AUTH_TOKEN"),
Logger: pyroscope.StandardLogger,
Tags: map[string]string{"hostname": os.Getenv("HOSTNAME"), "environment": "test", "version": "1.0"},
})
if err != nil {
log.Fatalf("error starting pyroscope profiler: %v", err)
}
for {
memNormal()
memLeak()
time.Sleep(time.Second)
}
}
func memNormal() {
if len(demosNormal) > 600 {
time.Sleep(time.Nanosecond)
return
}
demosNormal = append(demosNormal, &demo{
slice: make([]byte, 4000),
})
}
func memLeak() {
now := time.Now()
if now.Sub(beforeTime).Minutes() > 60 {
demos = make([]*demo, 0, 1024)
println("clear cache data")
beforeTime = now
}
demos = append(demos, &demo{
slice: make([]byte, 4000),
})
}
该段代码包含三部分:
main函数:程序的入口,每秒执行一次memNormal函数与memLeak函数。main函数中还包含性能监控接入代码。
memNormal函数:模拟正常程序运行。在程序运行的前10分钟内,每秒增加4000字节内存;在后续时间段内,不再额外增加内存。
memLeak函数:模拟内存泄露情景。在程序运行的所有时间内,每秒增加4000字节内存。
模拟程序以1小时为1个周期,周期结束后将释放所有内存。
问题发现与排查过程
本排查过程基于上述示例程序的数据。
通过数据查询进行排查
您可以通过性能监控>数据查询页面,排查问题。更多信息,请参见数据查询。
选择元数据为
service:golang_leak_app
、language:go
、type:profile_mem
、valueTypes:inuse_space
。通过上述元数据,您可观测目标App使用的内存情况。
设置主时间范围为4小时。
选择environment和version标签。
完成上述设置后,您可以观测迷你图和火焰图,确认问题。
观察到迷你图曲线呈1小时周期状上升,符合模拟程序预期。如果真实场景内存泄露,将持续上涨。
说明如果您的程序较复杂,您可以通过表格检索功能高亮指定方法名,并通过火焰图交互定位。具体操作,请参见火焰图。
设置主时间范围为2023-03-07 12:11~2023-03-07 12:21,即一个周期的最开始10分钟,该阶段为正常申请使用内存阶段,可以看到memNormal函数与memLeak函数所占大小几乎一致。
设置主时间范围为2023-03-07 12:51~2023-03-07 13:01,即该周期的末尾阶段,该阶段为memNormal函数使用内存不再变化,但memLeak函数一直持续增加的阶段,可以看到此时memLeak函数的使用内存远超memNormal函数。
通过上述数据,可知此时段memLeak函数出现内存泄露问题。为了有更明显的对比,并调查内存泄露的严重程度,您可以单击快速对比,进入数据对比页面,对比该问题所在时段与过去时段的资源占用差异。
通过数据对比进行排查
您在数据查询页面,单击快速对比后,系统将同步您已完成的配置(元数据筛选条件、标签筛选条件、聚合策略、元数据时间范围、主时间范围)到数据对比页面。更多信息,请参见数据对比。
设置预设同比时间为过去30min。
观察并聚焦memNormal函数与memLeak函数的具体变化。
通过上述对比可知memNormal函数过去和现在的内存没有变化,但是占用总内存比例却减少了38%,同时memLeak函数的当前值比过去半小时的值涨了0.43 GB内存,占用总内存比例增加了37%。