Go语言:runtime.ReadMemStats(m *MemStats)读取到的内存相关指标的含义和区别

2 篇文章 0 订阅

MemStats是一个结构体,里面指标很多,常用的有:

  • HeapObjects:堆中已经分配的对象总数,GC内存回收后HeapObjects取值相应减小。
  • HeapAlloc:  堆中已经分配给对象的字节数,GC内存回收后HeapAlloc取值相应减小。
  • TotalAlloc:  堆中已经分配给对象的总的累计字节数,只增不减,GC内存回收后也不减小。
  • HeapSys: 从操作系统为堆申请到的字节数。
  • HeapIdle: 堆的闲置区间,包括已经归还给操作系统的物理字节数(HeapReleased)
  • HeapReleased: 已经归还给操作系统的物理字节数,是HeapIdle的子集。

通常来说,一个进程所占用的常驻内存集RSS略大于 HeapIdle - HeapReleased (特殊情况是只声明了变量并未赋值,详见后文)。这些指标都是虚拟地址空间(virtual address space)并不完全对应于物料内存,Go13.8好像还没有直接获取RSS的函数。

 

测试代码:

为了方便,命名一个变量HeapRetained = HeapIdle - HeapReleased。

在函数f()中声明并赋值一个200MB的数组,记录函数调用前后各项内存指标的变化。RSS通过ps命令获取:

while :
do
 ps aux | grep memcheck | grep -v " memcheck" | awk '{print systime()"\t"$6/1024}'
 sleep 1
done

memcheck.go 

package main

import (
	"fmt"
	_ "github.com/dustin/go-humanize"
	"log"
	"os"
	"os/signal"
	"runtime"
	_ "strconv"
	"syscall"
	"time"
)

func toMegaBytes(bytes uint64) float64 {
	return float64(bytes) / 1024 / 1024
}

func traceMemStats() {
	var ms runtime.MemStats
	runtime.ReadMemStats(&ms)
	var result = make([]float64, 7)
	result[0] = float64(ms.HeapObjects)
	result[1] = toMegaBytes(ms.HeapAlloc)
	result[2] = toMegaBytes(ms.TotalAlloc)
	result[3] = toMegaBytes(ms.HeapSys)
	result[4] = toMegaBytes(ms.HeapIdle)
	result[5] = toMegaBytes(ms.HeapReleased)
	result[6] = toMegaBytes(ms.HeapIdle - ms.HeapReleased)

	fmt.Printf("%d\t", time.Now().Unix())
	for _, v := range result {
		fmt.Printf("%.2f\t", v)
	}
	fmt.Printf("\n")
	time.Sleep(1 * time.Second)
}

func f() {
	traceMemStats()

	var container [200 * 1024 * 1024]byte
	for i := 0; i < 200*1024*1024; i++ {
		container[i] = 0
	}

	traceMemStats()
	container[0] = 0
	log.Printf("%d", len(container))
}
func main() {
	time.Sleep(1 * time.Second)
	log.Println("call f() from main")
	f()
	log.Println("back to main")

	runtime.GC() // 调用强制gc函数
	go func() {
		for {
			traceMemStats()
		}
	}()
	log.Println("done.")

	//信号阻塞避免进程退出
	idleConnsClosed := make(chan struct{})
	go func() {
		sigint := make(chan os.Signal, 1)
		signal.Notify(sigint, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
		data := <-sigint
		log.Printf("received signal: " + data.String())
		log.Printf("start to shutdown...")
		close(idleConnsClosed)
	}()
	<-idleConnsClosed
}

 前面几个点的数据:

 RSSHeapObjectsHeapAllocTotalAllocHeapSysHeapIdleHeapReleasedHeapRetained
15928061525.054693030.110.1163.7563.363.270.03
15928061535.15625311200.11200.11319.62119.15119.120.03
1592806154213.5082530.09200.12319.66319.23119.16200.07
1592806155213.1522720.1200.12319.53319.09122.97196.12
1592806156209.1682790.1200.12319.53319.09126.88192.22
1592806157205.1842860.1200.12319.53319.09130.78188.31

 可以看到HeapRetained和RSS的曲线基本吻合。HeapSys约等于HeapIdle + HeapAlloc。

需要说明的是HeapAlloc ,TotalAlloc ,HeapSys,HeapIdle,HeapReleased 这些指标严格的定义是操作系统虚拟地址空间(virtual address space)并不一定对应于物理内存。比如说把上面的测试代码f()中赋值部分去掉:

func f() {
	traceMemStats()

	var container [200 * 1024 * 1024]byte
	//for i := 0; i < 200*1024*1024; i++ {
	//	container[i] = 0
	//}

	traceMemStats()
	container[0] = 0
	log.Printf("%d", len(container))
}

可以看到,由ReadMemStats得到的各项指标和之前几乎完全一样而RSS却一直很小。这也证明了虚拟地址空间并不完全对应于物理内存。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值