Abel'Blog

我干了什么?究竟拿了时间换了什么?

0%

go-内存报表详解

简介

本文只用说明heap报表中字段的信息,并且写了python小脚本来分析heap中的内存使用情况。

如何阅读heap信息

golang中的MemStats数据结构是用于描述当前进程的内存消耗值。

概要统计信息

Alloc 分配给heap对象的内存大小。这个值和HeapAlloc相同。

TotalAlloc 累计分配给heap对象的内存大小。它的增长和Alloc是一样的,但是不会随着对象释放而降低。

Sys 从OS获取的全部内存大小。这个值就是下面XSys值的总和。Sys度量被Go的运行时使用的一些虚拟地址空间,用于存储heap,stacks,和其他的内部数据结构。 虚拟内存不会真实的分配物理内存,虽然通常这一切是在某个时刻发生。

Lookups 处理runtime时候使用的指针数量。通常用于运行时调试之用。

Mallocs 累计分配heap对象个数。还存活的对象数量就是Mallocs - Frees

Frees 累计释放heap对象个数。

堆统计信息

简介信息

介绍一些Go如何组织内存。如果是>32k的内存将会直接使用系统的内存分配和释放。Go分割heap的虚拟内存到spans,这种内存是连续区域的内存大小在8k或者更加大。一个span可能有下列3中状态:

idle容器中没有任何的对象或者数据。idle span背后的物理内存能被释放还给OS(但是虚拟地址空间并不会这么做),或者能被转换成in use或者stack span。

in use至少含有一个heap对象而且可能含有空闲的空间提供分配给heap对象。

stack span 提供给了goroutine 栈使用。栈span不会被视为堆的一部分。span可以被当成heap或者stack内存;但是不可能同时用于两种。

细则

HeapAlloc heap对象分配时使用的内存大小。分配的heap对象包含一切能被引用的的对象,也包含没有被引用还没有被GC回收的对象。特别的,HeapAlloc增长时伴随着heap对象的分配,降低时伴随heap内存清理和没被引用的对象被回收。清理渐进的发生于GC周期,所以二者处理都是同时在进行中,最终HeapAlloc的结果趋近于平滑(于之形成对比的锯齿的情况,发生在stop-the-world内存回收过程)。

HeapSys heap对象从OS分配内存空间。HeapSys度量的是heap使用的虚拟内存空间。这些虚拟机空间已经被分配,但是没有使用,这样的情况将不会消耗任何的物理内存,但是它变成未使用时(阅读等下将提到的HeapReleased量),当值将会变小,这种情况发生在虚拟内存对应的物理内存归还给OS。

HeapIdle idle spans的空间。里面没有任何的对象,这些span可以(或者已经)被归还给OS,或者他们可能被提供给heap分配器接着来分配,或者也可以提供给stack做内存。HeapIdle减去HeapReleased就等于将会归还给OS的空间,但是这块空间可能被runtime利用,以便于heap变大时候,无需再次从OS中申请更多内存。如果这个差值大于heap大小,预示着最近有一次瞬时的实时堆大小达到的峰值。

HeapInuse in-usespans占用的空间大小。至少有一个对象在这里。这种span只能被用于相同的内存尺寸对象的分配。HeapInuse-HeapAlloc等于当前还可以分配给特定尺寸对象内存量,但目前尚未使用到。这个是内存碎片的上限,但是通常这么做能更加高效的复用。

HeapReleased 归还给操作系统的物理内存。从idle spans归还给操作系统的总数内存块,尚未为堆重新获取。

HeapObjects 当前分配的heap对象数目。和HeapAlloc相似,此值增减发生在对象分配和堆整理而且不可达对象的清理。

栈信息

堆和栈内存是互相独立的,在go运行时中,能将二者的内存互相转换。

细则

StackInuse 栈spans中使用全部内存块

StackSys 栈从OS中获取的内存块。StackSys是StackInuse加上少量直接从系统获取的空间为系统级线程的栈。

非堆栈内存信息

下面部分是运行时内部的数据结构的内存使用情况,未从堆内存分配的结构(通常是因为它们是实现堆的一部分)。不像堆或者栈内存,这种内存有专属的数据结构。

这些内存主要用于调试运行时内存。

细则

MSpanInuse / MSpanSys 分配mspan的内存,从系统中获取的空间。

MCacheInuse / MCacheSys 分配mcache的内存,从系统中获取的空间。

BuckHashSys 分析hash桶表大小。

GCSys gc元数据。

OtherSys 杂项。

为了方便,我写了一个读取的脚本。源码下载地址

这里我分析了一份内存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
概要信息
------------------
堆使用空间: 1.2G
累计分配堆空间: 10.3G
从系统分配的虚存: 0B
堆累计分配obj个数: 228.4M
堆累计归还obj个数: 213.1M
存活obj个数: 15.3M


堆信息
------------------
堆span空间中被obj用的空间: 1.2G
堆span累计空间: 1.8G
堆空闲span空间: 428.6M
堆span的全部空间: 1.4G
堆span中obj碎片空间: 188.1M
从堆span归还给OS空间: 1000.0K
当前内存中obj个数: 15.3M
全部堆内存使用: 1.8G


栈信息
------------------
栈spans中使用全部内存块: 1.3M
栈从OS中获取的内存块: 1.3M


非堆栈内存
------------------
mspan使用内存: 23.5M
mspan从OS中获取的内存块: 30.2M
mcache使用内存: 4.7K
mcache从OS中获取的内存块: 16.0K
分析hash桶表大小: 1.6M
gc元数据: 81.9M
杂项: 4.2M

heap分布I3FDiD.png

参考

[1] 实时监控heap使用情况的工具
[2] go-echat直接使用go绘图