对于go语言在运行时的一些内存分配,想要详细的了解,我们会用到自带的runtime.MemStats,有很多具体的细节实现,而不是简单的只看任务管理器中的内存分配。
我们先来看下这个记录内存分配器的结构体
type MemStats struct {
Alloc uint64 #堆空间分配的字节数
TotalAlloc uint64 #服务运行一直累积的总分配的堆空间,释放也不减少
Sys uint64 #操作系统获取的内存
Lookups uint64 #运行时执行的指针查找数,用于调试
Mallocs uint64 #服务malloc的次数
Frees uint64 #释放堆对象字节数
HeapAlloc uint64 #服务分配的堆内存字节数
HeapSys uint64 #系统分配的堆内存字节数
HeapIdle uint64 #申请但未分配的堆内存或者回收了的堆内存字节数
HeapInuse uint64 #正在使用的堆内存字节数
HeapReleased uint64 #返回给OS的堆内存,类似C/C++中的free
HeapObjects uint64 #堆内存块申请的量
StackInuse uint64 #正在使用的栈字节数
StackSys uint64 #系统分配的作为运行栈的内存
MSpanInuse uint64 #用于测试用的结构体使用的字节数
MSpanSys uint64 #系统为测试用的结构体分配的字节数
MCacheInuse uint64 #结构体申请的字节数,不被视为垃圾回收
MCacheSys uint64 #操作系统申请的堆空间用于mcache的量
BuckHashSys uint64 #用于剖析桶散列表的堆空间
GCSys uint64 #垃圾回收标记元信息使用的内存
OtherSys uint64 #golang系统架构占用的额外空间
NextGC uint64 #垃圾回收器检视的内存大小
LastGC uint64 #垃圾回收器最后一次执行时间
PauseTotalNs uint64 #垃圾回收或者其他信息收集导致服务暂停的次数
PauseNs [256]uint64 #一个循环队列,记录最近垃圾回收系统中断的时间
PauseEnd [256]uint64 # 一个循环队列,记录最近垃圾回收系统中断的时间开始点
NumGC uint32 #垃圾回收完成的次数
NumForcedGC uint32 #服务调用runtime.GC()强制使用垃圾回收的次数
// GODEBUG=gctrace=1.
GCCPUFraction float64 #垃圾回收占用服务CPU工作的时间总和
#如果有100个goroutine,垃圾回收的时间为1s,总占用100s
EnableGC bool #是否启用GC,如果启用,即便GOGC=off也是启用的
DebugGC bool
#内存分配器使用情况
BySize [61]struct {
Size uint32
Mallocs uint64
Frees uint64
}
}
看个具体的示例:
package main
import (
"fmt"
"runtime"
"time"
)
// 内存使用情况
func PrintMemUsage() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc))
fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc))
fmt.Printf("\tSys = %v MiB", bToMb(m.Sys))
fmt.Printf("\tNumGC = %v\n", m.NumGC)
}
// 转国际标(兆字节)
func bToMb(b uint64) uint64 {
return b / 1024 / 1024
}
func main() {
var s []string
for i := 0; i < 100000000; i++ {
s = append(s, "你好,寅恪光潜!")
}
PrintMemUsage()
for {
//runtime.GC() //调用GC进行内存回收
PrintMemUsage()
fmt.Println(s[0])
time.Sleep(time.Second * 1)
}
}
/*
C:\Users\Tony>go run test.go
Alloc = 5625 MiB TotalAlloc = 9192 MiB Sys = 8066 MiB NumGC = 25
Alloc = 4486 MiB TotalAlloc = 9192 MiB Sys = 8066 MiB NumGC = 26
你好,寅恪光潜!
Alloc = 4486 MiB TotalAlloc = 9192 MiB Sys = 8066 MiB NumGC = 26
你好,寅恪光潜!
Alloc = 4486 MiB TotalAlloc = 9192 MiB Sys = 8066 MiB NumGC = 26
你好,寅恪光潜!
Alloc = 4486 MiB TotalAlloc = 9192 MiB Sys = 8066 MiB NumGC = 26
...
*/
然后去掉runtime.GC()注释,启用内存回收,出现如下效果:
/*
C:\Users\Tony>go run test.go
Alloc = 4486 MiB TotalAlloc = 9192 MiB Sys = 7050 MiB NumGC = 26
Alloc = 1838 MiB TotalAlloc = 9192 MiB Sys = 7050 MiB NumGC = 27
你好,寅恪光潜!
Alloc = 1838 MiB TotalAlloc = 9192 MiB Sys = 7050 MiB NumGC = 28
你好,寅恪光潜!
Alloc = 1838 MiB TotalAlloc = 9192 MiB Sys = 7050 MiB NumGC = 29
你好,寅恪光潜!
Alloc = 1838 MiB TotalAlloc = 9192 MiB Sys = 7050 MiB NumGC = 30
...
*/
上面选取了几个成员变量,可以看出这个运行时的内存查看还是挺方便详细的。
有时候需要做内存压力测试等,我们可能会用到消耗一定的内存,没有Linux纯环境,我在WSL中运行,如果你也是Windows系统想要熟悉Linux也可以查阅:Windows版的Linux子系统(WSL)安装
malloc_mb.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define UNIT (1024*1024)
int main(int argc, char *argv[])
{
long long i = 0;
int size = 0;
if (argc != 2)
{
printf("请输入需要分配的内存值\n");
return 1;
}
#unsigned long int strtoul(const char *nptr, char **endptr, int base);
#unsigned long long int strtoull(const char *nptr, char **endptr,int base);
size = strtoull(argv[1], NULL, 10);#字符串转换成数字,十进制
if (size == 0)
{
printf("请输入大于0的内存值");
return 1;
}
char *buff = (char *) malloc(size * UNIT);
if (buff)
printf("已分配%dMB\n", size);
buff[0] = 1;
for (i = 1; i < (size * UNIT); i++)
{
if (i%1024 == 0)
buff[i] = buff[i-1]/8;
else
buff[i] = i/2;
}
pause();
}
编译成一个可执行文件:gcc malloc_mb.c -o malloc,目录下面将有个malloc文件,然后运行它:./malloc 1000
这样就会成功分配1000MB的内存!