共计 6819 个字符,预计需要花费 18 分钟才能阅读完成。
1.GODEBUG 命令运行程序,跟踪内存信息
命令 GODEBUG='gctrace=1' ./snippet_mem
用于启用 Go 程序的调试和性能剖析。当你设置 GODEBUG=’gctrace=1′ 启用 gctrace 时,Go 程序将输入与垃圾收集器相干的调试信息。这对于分析程序的内存治理和性能问题十分有用。
输入信息:
gc # @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #->#-># MB, # MB goal, # P
含意:
gc # GC 次数的编号,每次 GC 时递增
@#s 间隔程序开始执行时的工夫
#% GC 占用的执行工夫百分比
#+...+# GC 应用的工夫
#->#-># MB GC 开始,完结,以及以后沉闷堆内存的大小,单位 M
# MB goal 全局堆内存大小
# P 应用 processor 的数量
2. runtime 代码打印内存信息
2.1runtime.MemStats
也能够利用 runtime 库里的 ReadMemStats()办法, 读取的内存信息会保留在 runtime.MemStats
这个构造体中,能够从这个构造体中获取以后的内存信息,
// MemStats 记录无关内存分配器的统计信息。type MemStats struct {
// 通用统计信息。// Alloc 示意调配的堆对象的字节数。//
// 这与 HeapAlloc(见下文)雷同。Alloc uint64
// TotalAlloc 示意为堆对象累积调配的字节数。//
// TotalAlloc 随着堆对象的调配而减少,但
// 与 Alloc 和 HeapAlloc 不同,当
// 对象被开释时,它不会缩小。TotalAlloc uint64
// Sys 示意从操作系统获取的内存总字节数。//
// Sys 是上面的 XSys 字段的总和。Sys 度量
// 用于 Go 运行时的堆、栈和其余外部数据结构的虚拟地址空间。// 在任何给定时刻,可能并非所有虚拟地址空间都由物理内存反对,// 只管总体上它已经都是。Sys uint64
// Lookups 是运行时执行的指针查找次数。//
// 这次要用于调试运行时外部。Lookups uint64
// Mallocs 是累积的堆对象调配计数。// 存活对象的数量是 Mallocs - Frees。Mallocs uint64
// Frees 是累积的堆对象开释计数。Frees uint64
// 堆内存统计信息。// HeapAlloc 示意调配的堆对象的字节数。//
//“已调配”的堆对象包含所有可达对象,// 以及垃圾收集器尚未开释的不可达对象。// 具体来说,HeapAlloc 随着堆对象的调配而减少,// 随着堆被扫描和不可达对象被开释而缩小。// 扫描在 GC 周期之间逐增地进行,因而这两个过程
// 同时进行,因而 HeapAlloc 偏向于平滑变动
//(与进行 - 世界垃圾收集器的典型锯齿形相比)。HeapAlloc uint64
// HeapSys 示意从操作系统获取的堆内存字节数。//
// HeapSys 度量堆的虚拟地址空间的数量
// 为堆保留。这包含虚拟地址空间
// 曾经保留但尚未应用,不耗费物理内存,// 但通常很小,以及虚拟地址空间,其中
// 物理内存在变得未应用后曾经返回给了 OS
//(请参见 HeapReleased 以获取后者的度量)。//
// HeapSys 预计了堆的最大大小。HeapSys uint64
// HeapIdle 示意闲暇(未应用)span 中的字节数。//
// 闲暇 span 中不蕴含任何对象。这些 span 能够
//(并且可能曾经)返回给操作系统,或者能够
// 用于堆调配,或者能够从新用于
// 堆外存储器。//
// HeapIdle 减去 HeapReleased 预计了内存量
// 能够返回给操作系统,但因为运行时保留
// 能够增大堆而不须要从 OS 申请更多内存。// 如果这个差别显著大于堆大小,这表明最近
// 存活堆大小的短暂峰值。HeapIdle uint64
// HeapInuse 示意应用中 span 中的字节数。//
// 应用中 span 至多蕴含一个对象。这些 span
// 只能用于大致相同大小的其余对象。//
// HeapInuse 减去 HeapAlloc 预计了内存量
// 已调配给特定大小类,但以后未应用。// 这是对碎片的下限,但通常能够无效地重用
// 这段内存。HeapInuse uint64
// HeapReleased 示意返回给操作系统的物理内存字节数。//
// 这计算了从曾经返回给操作系统的闲暇 span 中的堆内存。HeapReleased uint64
// HeapObjects 是已调配的堆对象数量。//
// 与 HeapAlloc 一样,这会随着对象的调配而减少,// 随着堆的扫描和不可达对象的开释而缩小。HeapObjects uint64
// 栈内存统计信息。//
// 堆栈不被视为堆的一部分,但运行时
// 能够从新应用堆内存的 span 来进行堆栈内存,反之亦然。// StackInuse 示意应用中的 stack span 中的字节数。//
// 应用中的 stack span 至多蕴含一个 stack。这些
// span 只能用于雷同大小的其余 stack。//
// 不存在 StackIdle,因为未应用的 stack span
// 返回到堆(因而计入 HeapIdle)。StackInuse uint64
// StackSys 示意从操作系统获取的 stack 内存字节数。//
// StackSys 是 StackInuse,加上间接从操作系统取得的任何内存
// 用于 OS 线程堆栈。//
// 在非 cgo 程序中,此度量规范目前等于 StackInuse
//(但不应依赖,该值可能在未来更改)。//
// 在 cgo 程序中,此度量规范包含间接从操作系统调配的 OS 线程堆栈。// 目前,这仅在 c -shared 和 c -archive 构建模式下占用一堆的内存,// 并且来自 OS 的其余堆栈(尤其是由 C 代码调配的)目前不会被测量。// 请留神,这也可能会在未来更改。StackSys uint64
// 堆外存储器统计信息。//
// 以下统计信息测量了运行时内部结构
// 这些构造不是从堆内存调配的(通常不是这样,// 因为它们是堆的一部分的实现)。与
// 堆或堆内存不同,调配给这些
// 构造的内存是专门用于这些构造的。//
// 这些次要用于调试运行时内存开销。// MSpanInuse 示意调配的 mspan 构造的字节数。MSpanInuse uint64
// MSpanSys 示意从操作系统获取的 mspan 构造的内存字节数。MSpanSys uint64
// MCacheInuse 示意调配的 mcache 构造的字节数。MCacheInuse uint64
// MCacheSys 示意从操作系统获取的 mcache 构造的内存字节数。MCacheSys uint64
// BuckHashSys 示意散布桶哈希表中的内存字节数。BuckHashSys uint64
// GCSys 示意垃圾回收元数据中的内存字节数。GCSys uint64
// OtherSys 示意杂项堆外运行时调配的内存字节数。OtherSys uint64
// 垃圾收集器统计信息。// NextGC 是下一个 GC 周期的指标堆大小。//
// 垃圾回收器的指标是使 HeapAlloc ≤ NextGC。// 在每个 GC 周期完结时,依据可达数据的数量和
// GOGC 的值来计算下一个周期的指标。NextGC uint64
// LastGC 是上一个垃圾回收实现的工夫,以
// 纳秒为单位自 1970 年以来的工夫(UNIX 纪元)。LastGC uint64
// PauseTotalNs 示意自程序启动以来 GC 的
// 进行 - 世界暂停的累积纳秒数。//
// 在进行 - 世界暂停期间,所有 goroutine 都被暂停
// 只有垃圾回收器能够运行。PauseTotalNs uint64
// PauseNs 是最近 GC 进行 - 世界暂停工夫的循环缓冲区
// 以纳秒为单位。//
// 最近的暂停位于 PauseNs[(NumGC+255)%256]。// 通常,PauseNs[N%256]记录了在最
// 近的 N%256th GC 周期中暂停的工夫。在一个周期中
// 可能有屡次暂停;这是一个循环缓冲区中所有暂停的总和。PauseNs [256]uint64
// PauseEnd 是最近 GC 暂停完结工夫的循环缓冲区,// 以纳秒为单位自 1970 年以来的工夫(UNIX 纪元)。//
// 这个缓冲区的填充形式与 PauseNs 雷同。// 在一个周期中可能有屡次暂停;这记录了
// 在一个周期中最初一个暂停的完结。PauseEnd [256]uint64
// NumGC 是已实现的 GC 周期数。NumGC uint32
// NumForcedGC 是应用程序调用 GC 函数强制进行的 GC 周期数。NumForcedGC uint32
// GCCPUFraction 是自程序启动以来
// GC 应用的可用 CPU 工夫的分数。//
// GCCPUFraction 示意为 0 到 1 之间的数字,// 其中 0 示意 GC 没有应用该程序的 CPU。程序的
// 可用 CPU 工夫定义为自程序启动以来的 GOMAXPROCS 积分。// 也就是说,如果 GOMAXPROCS 为 2,并且程序运行了
// 10 秒钟,那么它的“可用 CPU”为 20 秒。GCCPUFraction
// 不包含用于写入屏障流动的 CPU 工夫。//
// 这与 GODEBUG=gctrace= 1 报告的 CPU 工夫雷同。GCCPUFraction float64
// EnableGC 示意 GC 是否已启用。它始终为 true,// 即便 GOGC=off。EnableGC bool
//...
}
2.2 示例代码
package main
import (
"log"
"runtime"
"time"
)
func readMemStats() {
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
log.Printf("===> Alloc:%d(bytes) HeapIdle:%d(bytes) HeapReleased:%d(bytes)", ms.Alloc, ms.HeapIdle, ms.HeapReleased)
}
func test() {
//slice 会动静扩容,用 slice 来做堆内存申请
container := make([]int, 8)
log.Println("===> loop begin.")
for i := 0; i < 32*1000*1000; i++ {container = append(container, i)
if (i == 16*1000*1000) {readMemStats()
}
}
log.Println("===> loop end.")
}
func main() {log.Println("===> [Start].")
readMemStats()
test()
readMemStats()
log.Println("===> [force gc].")
runtime.GC() // 强制调用 gc 回收
log.Println("===> [Done].")
readMemStats()
go func() {
for {readMemStats()
time.Sleep(10 * time.Second)
}
}()
time.Sleep(3600 * time.Second) // 睡眠,放弃程序不退出
}
能够看到,打印[Done]. 之后那条 trace 信息,Alloc 曾经降落,即内存已被垃圾回收器回收。在 2020/03/02 18:21:38 和 2020/03/02 18:21:48 的两条 trace 信息中,HeapReleased 开始回升,即垃圾回收器把内存归还给零碎。
3. pprof 工具收集性能指标
你能够应用 pprof 包来查看程序的内存状况。pprof 包提供了用于性能剖析的工具,包含查看内存分配情况的性能。pprof 有两种形式获取程序运行数据的工具,别离对应两个规范库:
runtime/pprof: 采集工具型利用运行数据进行剖析
net/http/pprof: 采集服务型利用运行时数据进行剖析
// 如果要定制一些采集信息能够通过 runtime 开启
runtime.SetBlockProfileRate(1) // 开启对阻塞操作的跟踪,block
runtime.SetMutexProfileFraction(1) // 开启对锁调用的跟踪,mutex
3.1 服务型利用应用 net/http/pprof
- 导入 net/http/pprof 包以及 net/http 包,以便启动一个 HTTP 服务器来提供 pprof 的 Web 接口。
import (
_ "net/http/pprof"
"net/http"
)
-
在你的代码中增加一个 HTTP 服务器,以便可能通过 Web 接口拜访 pprof 数据
go func() { // 监听 HTTP 申请,启动一个 HTTP 服务器 // 留神:这是一个示例,你能够依据须要更改端口 http.ListenAndServe("localhost:6060", nil) }()
- 服务型的在浏览器中输出地址:http://127.0.0.1:6060/debug/pprof/,即可看到监控的数据。
3.2 工具型利用应用 runtime/pprof
- 在你的代码中的适当地位导入 runtime/pprof 包并应用它来生成内存剖析数据。
import (
"runtime/pprof"
"os"
)
func main() {
// 创立一个文件,用于存储内存剖析数据
memFile, _ := os.Create("memory.pprof")
defer memFile.Close()
// 开始内存剖析,将后果写入文件
pprof.WriteHeapProfile(memFile)
// 你的利用程序代码
}
3.3 剖析 profile
3.3.1 工具型模式
执行go tool pprof app.profile
,会进入一个命令行交互页面,能够输出命令查看须要查看的信息.
3.3.2 服务端模式
- 形式一:
间接应用 web 服务接口数据go tool pprof http:127.0.0.1:6060/debug/pprof/profile?seconds=30
- 形式二:
先从 web 服务接口下载 profile 文件wget -O app.profile 'http:127.0.0.1:6060/debug/pprof/profile?seconds=30'
而后剖析下载 profile 文件,执行go tool pprof app.profile
,会进入一个命令行交互页面。
3.3.3 交互命令
执行 go tool pprof app.profile
后,能够通过 help 来看反对的命令,比方输出 help 能够看到有 top 命令,通过输出 top 来查看 cpu 的性能状况.
//flat:以后函数占用 CPU 的耗时
//flat:: 以后函数占用 CPU 的耗时百分比
//sun%:函数占用 CPU 的耗时累计百分比
//cum:以后函数加上调用以后函数的函数占用 CPU 的总耗时
//cum%:以后函数加上调用以后函数的函数占用 CPU 的总耗时百分比
// 最初一列:函数名称
(pprof) top
Showing nodes accounting for 17.57s, 92.86% of 18.92s total
Dropped 147 nodes (cum <= 0.09s)
Showing top 10 nodes out of 48
flat flat% sum% cum cum%
15.62s 82.56% 82.56% 15.64s 82.66% runtime.cgocall
0.38s 2.01% 84.57% 0.48s 2.54% math/rand.(*Rand).Int31n
0.29s 1.53% 86.10% 16.52s 87.32% internal/poll.(*FD).writeConsole
0.25s 1.32% 87.42% 0.25s 1.32% runtime.unlock2
0.24s 1.27% 88.69% 0.35s 1.85% bytes.(*Buffer).Write
0.20s 1.06% 89.75% 0.31s 1.64% unicode/utf16.Encode
0.19s 1.00% 90.75% 0.19s 1.00% runtime._ExternalCode
0.16s 0.85% 91.60% 0.79s 4.18% math/rand.Intn
0.13s 0.69% 92.28% 0.19s 1.00% runtime.lock2
0.11s 0.58% 92.86% 0.11s 0.58% runtime.stdcall2
(pprof)
4. 应用 graphviz 可视化
首先要下载 graphviz,https://graphviz.org/download/ 装置对应的版本。
形式一:这时候应用命令go tool pprof app.profile
, 输出 web 指令,就能够查看对应的其可视化的剖析后果了。
形式二:间接通过命令 go tool pprof -http=:8080 app.profile
启动 web 在浏览器中可视化查看后果。