在日常工作中,对于内存的监控也是不可缺失的一环,本节将从内存的监控原理和计算过程,两个方面介绍如何实现对于内存使用率的监控。
原理
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:缓冲的量,是指内核为了加速文件访问而使用的内存区域。当系统读取文件时,它会将文件的一部分内容(或全部)存储在缓存中。如果后续有对同一文件的读取请求,系统可以直接从缓存中提供数据,而不需要重新从磁盘读取,这样可以显著提高文件访问的速度。
计算过程
计算内存使用率的逻辑基于以下几个步骤:
- 确定总内存: 首先,我们需要知道系统的总内存量。这可以通过读取/proc/meminfo文件中的MemTotal字段获得。
- 计算已使用内存: 已使用内存是总内存减去空闲内存、缓存和缓冲区的内存。这些值也可以从/proc/meminfo文件中获得。具体来说,MemFree字段表示空闲内存,Buffers和Cached字段分别表示用作缓冲和缓存的内存。
- 计算内存使用率: 一旦我们有了总内存和已使用内存的数值,我们可以通过将已使用内存除以总内存,然后乘以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%