Golang性能优化比较受欢迎的方式是go tool,可以非常直观的把内存、cpu等资源展现给用户。
由于我司对生产环境的限制,无法暴露pprof的端口。这导致导致我只能dump一份profile文件进行分析(http://{ip}:{port}/debug/pprof/heap?debug=1)。
这份纯文本信息的profile对于智商低下又没有耐心的我来讲,wqnmlgb的。因此对这份操蛋的文本整理了一下。
直接上文本:
1 | heap profile: 3190: 77516056 [54762: 612664248] @ heap/1048576 |
以上是我在互联网上copy的一份profile文本,接下来我们详细研究研究。
主要分为三大部分:进程heap的整体使用情况、MemProfileRecord、MemStats信息。
1、进程heap的整体使用情况。
1 | heap profile: 3190: 77516056 [54762: 612664248] @ heap/1048576 |
从heap profile后开始看。
- {num1}: {num2} 冒号前的数字num1表示进程中正在使用的对象个数,冒号后的数字num2表示进程中正在使用的内存大小(单位byte)。
- [{num3}: {num4}] 中括号里表示的当前进程分配信息,冒号前的数字num3表示进程中分配的对象个数,冒号后的数字num4表示进程中分配的内存空间大小(单位byte)。
- @ heap/{num5} num5的值是 2*MemProfileRate,runtime.MemProfileRate为采样率。控制内存profile记录的内存分配的采样频率,内存profile记录器平均每分配MemProfileRate字节进行一次分配采样。
2、MemProfileRecord信息
MemProfileRecord用于描述某个调用栈序列申请和释放的活动对象等信息。在dump出来的文本中,MemProfileRecord信息信息占据了绝大部分比重。
1 | 1: 29081600 [1: 29081600] @ 0x89368e 0x894cd9 0x8a5a9d 0x8a9b7c 0x8af578 0x8b4441 0x8b4c6d 0x8b8504 0x8b2bc3 0x45b1c1 |
第一行同heap profile信息表述大体相同。
- {num1}: {num2} 冒号前的数字num1表示当前调用栈正在使用的对象个数,冒号后的数字num2表示当前调用栈正在使用的内存大小(单位byte)。
- [{num3}: {num4}] 中括号里表示的当前调用栈分配信息,冒号前的数字num3表示当前调用栈分配的对象个数,冒号后的数字num4表示当前调用栈分配的内存空间大小(单位byte)。
- @ 后面的十六进制集合表示的是当前调用栈的栈针指向地址。
其余以#开始的几行表示的是栈信息,其格式为有两种形式(可能版本不同有少许差距)
- # {frame.PC}
frame.PC 表示这个帧中位置的程序计数器。对于调用另一帧的帧,这将是调用指令的程序计数器。由于内联,多个帧可能具有相同的PC值,但不同的符号信息。 - # {frame.PC} {name}+{frame.PC-frame.Entry} {frame.File}:{frame.Line}
frame.PC 同上
name 此调用帧的包路径限定函数名。
frame.Entry 方法的入口程序计数器。
frame.File 文件名。
frame.Line 在此帧中文件的行号。
3、MemStats信息
通过runtime.ReadMemStats()读取的runtime.MemStats信息
- Alloc:是已分配堆对象的字节。这与HeapAlloc相同。
- TotalAlloc :为堆对象分配的累积字节。TotalAlloc在分配堆对象时增加,但与Alloc和HeapAlloc不同,它在释放对象时不会减少。
- Sys:从操作系统获得的内存的总字节数。是下面XSys字段的总和。Sys测量Go运行时为堆、栈和其他内部数据结构保留的虚拟地址空间。尽管通常情况下,并不是所有的虚拟地址空间都由物理内存支持这一切都发生在某个时刻。
- Lookups:由运行时执行的指针查找数。这对于调试运行时内部非常有用。
- Mallocs:分配的堆对象的累计计数。活动对象的数量是Mallocs - free。
- Frees:释放的堆对象的累计计数。
- HeapAlloc:已分配堆对象的字节。“已分配”堆对象包括所有可访问对象,以及垃圾收集器尚未释放的不可访问对象。具体地说,HeapAlloc随着堆对象的分配而增加,随着堆被扫描而减少无法访问的对象被释放。清扫在GC周期之间以增量方式进行,所以这两个进程同时发生,因此HeapAlloc趋向于平稳地变化(与典型的停止世界垃圾收集器的锯齿形成对比)。
- HeapSys:从操作系统获得的堆内存字节。HeapSys度量为堆保留的虚拟地址空间量。这包括虚拟地址空间被保留但尚未使用,消耗没有物理内存,但往往是小,以及虚拟地址空间的物理内存已经返回给操作系统后就未使用(见HeapReleased衡量后者)。HeapSys估计堆的最大大小。
- HeapIdle:是idle(未使用的)范围中的字节。idle没有对象。这些span可以(可能已经)返回到操作系统,或者可以将它们用于堆分配,或者将它们作为堆栈内存重用。HeapIdle减去heaprelease估计了可以返回给操作系统的内存数量,但是运行时保留了这些内存,所以它可以在不向操作系统请求更多内存的情况下增加堆。如果这个差异比堆大小大得多,则表明最近有一个动态堆大小的暂态峰值。
- HeapInuse:正在使用的span中的字节。正在使用的span中至少有一个对象。这些跨度只能用于大致相同大小的其他对象。HeapInuse - HeapAlloc估计了专门用于特定大小类的内存总量,但目前还没有使用。这是碎片的上限,但通常可以有效地重用这些内存。
- HeapReleased:返回给操作系统的物理内存字节。这将计算从已返回到操作系统且尚未为堆重新获取的空闲区段中获得的堆内存。
- HeapObjects:分配的堆对象的数量。与HeapAlloc类似,这在分配对象时增加,在清理堆和释放不可达对象时减少。
- Stack:{StackInuse} / {StackSys}
StackInuse:栈内存统计信息。栈不被认为是堆的一部分,但是运行时可以为堆栈内存重用一段堆内存,反之亦然。StackInuse是堆栈范围中的字节。正在使用的堆栈范围中至少有一个堆栈。这些跨度只能用于相同大小的其他堆栈。不存在StackIdle,因为未使用的堆栈跨度被返回到堆(因此计算为HeapIdle)。
StackSys:从操作系统获得的堆栈内存字节。StackSys是StackInuse,加上直接从OS获取的用于OS线程堆栈的内存(应该是最小的)。 - MSpan:{MSpanInuse} / {MSpanSys}
MSpanInuse:堆内存统计信息。下面的统计数据度量没有从堆内存中分配的运行时内部结构(通常因为它们是实现堆的一部分)。与堆或堆栈内存不同,分配给这些结构的任何内存都专用于这些结构。这些主要用于调试运行时内存开销。MSpanInuse是分配给mspan结构的字节。
MSpanSys:为mspan结构从操作系统获得的内存字节。 - MCache:{MCacheInuse} / {MCacheSys}
MCacheInuse:已分配的mcache结构的字节数。
MCacheSys:从操作系统为mcache结构获取的内存字节。 - BuckHashSys:分析桶哈希表的内存字节。
- GCSys:垃圾收集元数据中的内存字节。
- OtherSys:各种堆外运行时分配中的内存字节。
- NextGC:下一个GC周期的目标堆大小。垃圾收集器的目标是保持HeapAlloc≤NextGC。在每个GC周期结束时,根据可到达的数据量和GOGC的值计算下一个周期的目标。
- LastGC:自1970年(UNIX时代)以来最后一次垃圾收集完成的时间,以纳秒为单位。
- PauseNs:一个循环缓冲区,它以纳秒为单位记录最近的GC STW暂停时间。最近一次暂停是在PauseNs[(NumGC+255)%256]。通常,PauseNs[N%256]记录最近的N%256个GC周期中暂停的时间。每个GC周期可能有多个暂停;这是一个循环中所有暂停的总和。
- PauseEnd:是一个循环缓冲区,用于记录最近的GC暂停结束时间,从1970年(UNIX时代)开始,以纳秒为单位。这个缓冲区的填充方式与停顿相同。每个GC周期可能有多个暂停;这将记录一个循环中最后一个暂停的结束时间。
- NumGC:完成的GC周期的数量。
- NumForcedGC:调用GC函数的应用程序强制执行的GC周期数。
- GCCPUFraction:程序启动以来GC使用的可用CPU时间的分数。GCCPUFraction表示0到1之间的一个数字,其中0表示GC没有消耗任何程序的CPU。一个程序的可用CPU时间被定义为自程序启动以来GOMAXPROCS的积分。也就是说,如果GOMAXPROCS是2,并且程序已经运行了10秒,那么它的“可用CPU”是20秒。GCCPUFraction不包括用于写屏障活动的CPU时间。这与GODEBUG报告的CPU部分=gctrace=1相同。
- DebugGC:目前未使用。