内存监控的深入探究

在日常工作中,对于内存的监控也是不可缺失的一环,本节将从内存的监控原理和计算过程,两个方面介绍如何实现对于内存使用率的监控。

原理

Linux 上记录当前内存情况的文件是 /proc/meminfo,内容如下:

MemTotal:        4158324 kB
MemFree:         3580068 kB
MemAvailable:    3645116 kB
Buffers:            2104 kB
Cached:           267512 kB
SwapCached:            0 kB
Active:           151100 kB
Inactive:         231864 kB
Active(anon):     114140 kB
Inactive(anon):    11264 kB
Active(file):      36960 kB
Inactive(file):   220600 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                64 kB
Writeback:             0 kB
AnonPages:        113412 kB
Mapped:            84392 kB
Shmem:             12056 kB
Slab:              92512 kB
SReclaimable:      27512 kB
SUnreclaim:        65000 kB
KernelStack:        5968 kB
PageTables:         4720 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     2079160 kB
Committed_AS:     627688 kB
VmallocTotal:   34359738367 kB
VmallocUsed:      197048 kB
VmallocChunk:   34359310332 kB
HardwareCorrupted:     0 kB
AnonHugePages:     59392 kB
CmaTotal:              0 kB
CmaFree:               0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:      114496 kB
DirectMap2M:     3334144 kB
DirectMap1G:     1048576 kB

在我们计算内存使用率的时候主要会涉及到以下几个参数:

  • MemTotal:总内存量
  • MemFree:剩余内存量
  • Buffers:缓冲区的量,指内核为了提高块设备(如硬盘、SSD等)I/O操作效率而使用的内存区域。当系统需要读取或写入数据到块设备时,数据会先被存储在缓冲区中。这样做的目的是为了减少对物理设备的直接访问次数,因为物理I/O操作通常比内存访问要慢得多。通过使用缓冲区,系统可以批量处理I/O请求,从而提高数据传输的效率和速度。
  • Cached:缓冲的量,是指内核为了加速文件访问而使用的内存区域。当系统读取文件时,它会将文件的一部分内容(或全部)存储在缓存中。如果后续有对同一文件的读取请求,系统可以直接从缓存中提供数据,而不需要重新从磁盘读取,这样可以显著提高文件访问的速度。

计算过程

计算内存使用率的逻辑基于以下几个步骤:

  1. 确定总内存: 首先,我们需要知道系统的总内存量。这可以通过读取/proc/meminfo文件中的MemTotal字段获得。
  2. 计算已使用内存: 已使用内存是总内存减去空闲内存、缓存和缓冲区的内存。这些值也可以从/proc/meminfo文件中获得。具体来说,MemFree字段表示空闲内存,Buffers和Cached字段分别表示用作缓冲和缓存的内存。
  3. 计算内存使用率: 一旦我们有了总内存和已使用内存的数值,我们可以通过将已使用内存除以总内存,然后乘以100来计算内存使用率的百分比。

下面是具体的计算逻辑:

// 计算已使用的内存
memStats.Used = memStats.Total - memStats.Free - memStats.Cached - memStats.Buffers

// 计算内存使用率
memStats.UsedPercent = (float64(memStats.Used) / float64(memStats.Total)) * 100

代码实现

下面将使用 golang 实现一个简单对内存监控的程序,代码如下:

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"
)

// MemoryStats 定义了内存统计信息的结构体
type MemoryStats struct {
	Total     uint64 `json:"total"`
	Free       uint64 `json:"free"`
	Available uint64 `json:"available"`
	Buffers   uint64 `json:"buffers"`
	Cached     uint64 `json:"cached"`
	Used       uint64 `json:"used"`       // 已使用的内存
	UsedPercent float64 `json:"usedPercent"` // 内存使用率
}

// updateMemoryStats 读取/proc/meminfo文件并更新内存统计信息
func updateMemoryStats() MemoryStats {
	var memStats MemoryStats

	// 打开/proc/meminfo文件
	file, err := os.Open("/proc/meminfo")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		fields := strings.Fields(line)

		// 移除单位"kB",只保留数字
		value := strings.TrimSuffix(fields[1], "kB")
		value = strings.TrimSuffix(value, "KB")

		// 将字符串转换为uint64类型的数值
		v, err := strconv.ParseUint(value, 10, 64)
		if err != nil {
			panic(err)
		}

		switch strings.TrimSpace(fields[0]) {
		case "MemTotal:":
			memStats.Total = v
		case "MemFree:":
			memStats.Free = v
		case "MemAvailable:":
			memStats.Available = v
		case "Buffers:":
			memStats.Buffers = v
		case "Cached:":
			memStats.Cached = v
		}
	}

	if err := scanner.Err(); err != nil {
		panic(err)
	}

	// 计算已使用的内存
	memStats.Used = memStats.Total - memStats.Free - memStats.Cached - memStats.Buffers

	// 计算内存使用率
	memStats.UsedPercent = (float64(memStats.Used) / float64(memStats.Total)) * 100

	return memStats
}

// printMemoryStats 打印内存统计信息
func printMemoryStats(memStats MemoryStats) {
	fmt.Printf("Memory Usage:\n")
	fmt.Printf("Total: %d kB\n", memStats.Total)
	fmt.Printf("Free: %d kB\n", memStats.Free)
	fmt.Printf("Available: %d kB\n", memStats.Available)
	fmt.Printf("Buffers: %d kB\n", memStats.Buffers)
	fmt.Printf("Cached: %d kB\n", memStats.Cached)
	fmt.Printf("Used: %d kB\n", memStats.Used)
	fmt.Printf("Usage Percentage: %.2f%%\n", memStats.UsedPercent)
}

func main() {
	// 定义一个通道用于停止监控
	quit := make(chan struct{})

	// 定期检查内存使用情况
	go func() {
		for {
			select {
			case <-quit:
				return
			default:
				memStats := updateMemoryStats()
				printMemoryStats(memStats)
				// 每隔5秒检查一次
				time.Sleep(5 * time.Second)
			}
		}
	}()

	// 等待用户输入,然后停止监控
	fmt.Println("Press Enter to exit...")
	fmt.Scanln()
	close(quit)
}
//运行结果如下
//Total: 4158324 kB
// Free: 3476436 kB
// Available: 3625224 kB
// Buffers: 2104 kB
// Cached: 349436 kB
// Used: 330348 kB
// Usage Percentage: 7.94%

后记

Node_exporter收集内存情况的代码段

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

启明真纳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值