Linux 进程内存监控:Linux 内存调优之进程内存深度监控

我看远山,远山悲悯

写在前面


  • 博文内容涉及 Linux 进程内存监控
  • 监控方式包括传统工具 ps/top/pmap ,以及 cgroup 内存子系统,proc 内存伪文件系统
  • 监控内容包括进程内存使用情况 内存全局数据统计内存事件指标,以及进程内存段数据监控
  • 理解不足小伙伴帮忙指正 :),生活加油

我看远山,远山悲悯

持续分享技术干货,感兴趣小伙伴可以关注下 ^_^


监控进程的内存使用量

这里分析的工具主要是原生工具,后面还会分享一些 BPF 相关的内存观察工具以及系统内存的全局监控

PS/TOP

一般的内存监控工具,对于进程级别的,会使用如 ps/top 命令, 通过指标 VIRTVSZRESRSS 来区分两种不同的统计数据

  • VIRTVSZ 代表进程申请的虚拟内存大小,
  • RESRSS 代表的是虚拟内存当前实际映射的物理内存大小,也叫常驻内存。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──[root@vms100.liruilongs.github.io]-[~]
└─$top
top - 11:46:44 up 8 min, 1 user, load average: 6.52, 17.45, 10.38
Tasks: 449 total, 1 running, 448 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.9 us, 1.1 sy, 0.0 ni, 96.8 id, 0.0 wa, 0.6 hi, 0.5 si, 0.0 st
MiB Mem : 15730.5 total, 7503.6 free, 7147.4 used, 1079.5 buff/cache
MiB Swap: 2068.0 total, 2068.0 free, 0.0 used. 8177.7 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1947 42418 20 0 555196 132972 20272 S 1.3 0.8 0:12.86 heat-engine
10320 42436 20 0 540276 122200 17492 S 1.3 0.8 0:08.95 nova-scheduler
949 root 20 0 694072 31640 16876 S 1.0 0.2 0:16.58 tuned
1945 42415 20 0 749212 142564 34988 S 1.0 0.9 0:12.38 glance-api
1965 42436 20 0 540120 121848 17324 S 1.0 0.8 0:12.16 nova-conductor
15567 root 20 0 269540 5228 4160 R 1.0 0.0 0:00.05 top
..........................................

所以如果通过上面的命令,查看应用实际使用的内存大小,需要查看 RES(RSS(KB单位)) 列,表示进程当前驻留在物理内存中的内存总量(即没有被交换到磁盘的部分)。

RSS 包含的内容​​:

  • 进程独有的数据(如堆、栈、私有匿名页)。
    ​​+ 共享内存页​​(如共享库、共享内存 IPC 等)。
1
2
3
4
5
6
7
8
9
10
11
12
┌──[root@liruilongs.github.io]-[~]
└─$ps -e -o pid,vsz,rss,comm | awk '$2 > 0 {print}'
PID VSZ RSS COMMAND
1 170808 14208 systemd
704 26824 11520 systemd-journal
719 34968 12160 systemd-udevd
892 18156 4540 auditd
913 10796 4736 dbus-broker-lau
。。。。。。。。。。
4177 6664 3712 awk
┌──[root@liruilongs.github.io]-[~]
└─$

共享内存

需要说明的是,进程是共享物理内存页帧的。比如使用相同库函数的两个进程,就可以共享使用相同的物理内存页来存储库文件代码。它们各自的 ​​RSS(Resident Set Size)​​ 值会将该共享页的物理内存(SHR 列)重复计入每个进程的 RSS(RES)。因此 ​​进程的 RSS 总和​​ 可能会明显超过 ​​系统实际的物理内存容量​​。真实物理内存占用 = ​​独占内存(RES) - 共享内存​SHR​​(Shared Memory)​。

SHR列 ​​:进程占用的 ​​共享物理内存​​(如共享库、共享内存 IPC)。

Cgroup 子系统

通过 Cgroup 子系统来获取内存信息,在获取之前需要获取当前进程的PID以及对应的 Cgroup 分组

获取 htop 进程ID, htop 是一个类似 top 的系统整体性能监控的进程

1
2
3
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$pgrep htop
4150

通过 ps 命令获取 htop 对应的 Cgroup 分组

1
2
3
4
5
6
7
8
9
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$ps -o cgroup 4150
CGROUP
11:pids:/user.slice/user-1000.slice/session-1.scope,
8:memory:/user.slice/user-1000.slice/session-1.scope,
2:devices:/user.slice,
1:name=systemd:/user.slice/user-1000.sli
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system]
└─$

我们只关注内存子系统的,所以直接看内存的分组: 8:memory:/user.slice/user-1000.slice/session-1.scope,这里的 8 表示 Cgroup 层级

可以通过下面的命令查看 Cgroup 层级以及 当前系统挂载了多少子系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 4 1 1
cpu 6 3 1
cpuacct 6 3 1
blkio 9 1 1
memory 8 91 1
devices 2 43 1
freezer 13 1 1
net_cls 12 1 1
perf_event 5 1 1
net_prio 12 1 1
hugetlb 7 1 1
pids 11 52 1
rdma 3 1 1
files 10 1 1
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system]
└─$

user.slice/user-1000.slice/session-1.scope 表示 Cgroup 层级树

  • user.slice​​:表示该 cgroup 属于 ​​用户会话层级​​,与用户进程相关(与 system.slice 系统服务层级区分)
  • user-1000.slice​​:表示用户 ID 为 ​​1000​​ 的普通用户(Linux 中 UID 1000 通常是首个创建的非 root 用户)
  • session-1.scope​​:表示该用户的 ​​会话单元​​(如一个终端会话或登录会话),属于临时性资源组(scope 用于管理短生命周期的进程组)

htop 是一个前台进程,通过 Cgroup 资源树可以很清晰的看到,下面来看下 Cgroup 内存子系统观察进程内存信息的一些指标文件

内存详细信息指标监控

下面一组是内存详细信息的数据统计

参数 作用
memory.numa_stat NUMA 节点的内存使用统计(适用于多 CPU 架构)。
memory.stat 详细内存使用统计

在这之前我们先介绍一个特殊的值,memory.limit_in_bytes 这个可能是我们接触最多的参数,用于进程 物理内存资源限制

1
2
3
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /sys/fs/cgroup/memory/user.slice/user-1000.slice/session-1.scope/memory.limit_in_bytes
9223372036854771712

可以看到上面的 htop, 文件中的值 9223372036854771712 表示当前 cgroup 的内存限制处于​​无限制状态​​,当 cgroup 未显式设置内存限制时,内核会默认将此值设为 PAGE_COUNTER_MAX,该值由内核通过 LONG_MAX / PAGE_SIZE * PAGE_SIZE 计算得出,确保与内存页对齐.

memory.stat

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
38
39
40
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /sys/fs/cgroup/memory/user.slice/user-1000.slice/session-1.scope/memory.stat
cache 393973760
rss 301170688
rss_huge 171966464
shmem 847872
mapped_file 86605824
dirty 8192
writeback 0
swap 0
pgpgin 241505
pgpgout 113977
pgfault 231305
pgmajfault 260
inactive_anon 302727168
active_anon 147456
inactive_file 348233728
active_file 44892160
unevictable 0
hierarchical_memory_limit 9223372036854771712
hierarchical_memsw_limit 9223372036854771712
total_cache 393973760
total_rss 301170688
total_rss_huge 171966464
total_shmem 847872
total_mapped_file 86605824
total_dirty 8192
total_writeback 0
total_swap 0
total_pgpgin 241505
total_pgpgout 113977
total_pgfault 231305
total_pgmajfault 260
total_inactive_anon 302727168
total_active_anon 147456
total_inactive_file 348233728
total_active_file 44892160
total_unevictable 0
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system]
└─$

以下是关键参数的说明


memory.stat 核心参数解析表

参数分类 参数名 值(字节) 说明 相关引用
基础内存使用 cache 393,973,760 文件缓存和 tmpfs/shmem 内存,用于加速文件访问(可回收)
rss 301,170,688 进程匿名内存(堆、栈等)占用,反映实际物理内存使用量
swap 0 当前 cgroup 使用的交换空间大小,非零表示物理内存不足
内存页管理 active_anon 147,456 活跃的匿名内存页(正在使用的堆、栈内存)
inactive_anon 302,727,168 非活跃的匿名内存页(可被回收的堆、栈内存)
active_file 44,892,160 活跃的文件缓存页(近期被频繁访问的文件数据)
inactive_file 348,233,728 非活跃的文件缓存页(长时间未访问的文件数据,优先回收)
内存事件 pgpgin 241,505 从磁盘换入内存的页数,高值可能反映频繁 I/O
pgpgout 113,977 从内存换出到磁盘的页数
pgmajfault 260 需磁盘 I/O 的硬缺页次数,高值可能引发性能问题
层级管理 hierarchical_memory_limit 9,223,372,036… 层级化内存限制(当前值为极大数,表示未启用限制)
total_* 系列(如 total_rss 与同名参数一致 包含当前 cgroup 及其子 cgroup 的总统计值(如 total_rss 表示层级内所有进程的匿名内存总和)
其他 rss_huge 171,966,464 透明大页(THP)占用量,大页可减少内存管理开销
unevictable 0 不可回收的内存(如 mlock 锁定的内存)

  1. 单位与换算:上述值均为 字节(Bytes),可通过 1 GB = 1073741824 Bytes 转换为更易读的单位。例如:
    cache = 393,973,760 Bytes ≈ 376 MB
    rss = 301,170,688 Bytes ≈ 287 MB
  2. 关键场景判断
    内存压力:若 rss 接近 hierarchical_memory_limit,需警惕 OOM(内存耗尽)风险。
    缓存优化:若 inactive_file 较高,可通过 sync; echo 3 > /proc/sys/vm/drop_caches 手动回收缓存。
  3. free/vmstat 的关系
    free -m 中的 buff/cache 对应 cache + buff(部分系统可能合并统计)。
    vmstatsi/so 对应 pgpgin/pgpgout 的实时动态变化。

如需进一步分析具体进程的内存行为,可结合 /proc/<PID>/smaps 或工具如 smem

memory.numa_stat

1
2
3
4
5
6
7
8
9
10
11
12
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /sys/fs/cgroup/memory/user.slice/user-1000.slice/session-1.scope/memory.numa_stat
total=169929 N0=169929
file=95978 N0=95978
anon=73951 N0=73951
unevictable=0 N0=0
hierarchical_total=169929 N0=169929
hierarchical_file=95978 N0=95978
hierarchical_anon=73951 N0=73951
hierarchical_unevictable=0 N0=0
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system]
└─$

以下是对 memory.numa_stat 输出参数的详细解释

参数名 类型 描述
total 基础统计项 当前 cgroup 在 NUMA 节点上的总内存占用(单位字节),等于 anon + file + unevictable 之和
file 基础统计项 文件页缓存(File-backed memory)占用量,例如程序文件、共享库等通过文件映射的内存
anon 基础统计项 匿名页(Anonymous pages)和 Swap 缓存的总量,例如堆、栈等动态分配的内存
unevictable 基础统计项 不可回收的内存页(例如被锁定或标记为不可回收的页面)
hierarchical_total 层级统计项 包含所有子 cgroup 的总内存占用(单位字节),统计范围覆盖当前 cgroup 及其子级
hierarchical_file 层级统计项 包含所有子 cgroup 的文件页缓存总量
hierarchical_anon 层级统计项 包含所有子 cgroup 的匿名页和 Swap 缓存总量
hierarchical_unevictable 层级统计项 包含所有子 cgroup 的不可回收内存总量

补充说明:

  1. NUMA 节点标识:输出中的 N0=169929 表示当前统计值属于 NUMA 节点 0(N0)。在 NUMA 架构中,每个节点的本地内存访问速度更快,跨节点访问会增加延迟。
  2. 层级统计的意义: 带 hierarchical_ 前缀的参数表示当前 cgroup 及其所有子 cgroup 的累积内存使用量。例如,hierarchical_total 是当前 cgroup 和子 cgroup 在所有 NUMA 节点上的内存总量。
  3. 应用场景:通过对比 totalhierarchical_total,可判断子 cgroup 的内存分配是否合理。若 unevictable 值较高,可能需排查是否有进程误用内存锁定(如 mlock)。

上面的输出信息中提供的输出中,total=169929 N0=169929 表示该 cgroup 仅在 NUMA 节点 0 上分配了 169,929 字节内存。其中:

• 文件页缓存占 95,978 字节(file=95978),
• 匿名页和 Swap 缓存占 73,951 字节(anon=73951),
• 无不可回收内存(unevictable=0)。

内存事件指标监控

下面为内存事件指标依次来看一下

参数 作用
memory.usage_in_bytes 当前物理内存使用量(包括匿名内存、文件缓存等)。
memory.memsw.usage_in_bytes 当前物理内存 + swap 总使用量
memory.failcnt 记录内存限制触发的失败次数(超出 memory.limit_in_bytes 的次数)。
memory.events 内存事件计数器(如 oom 溢出次数、under_oom 低内存状态)。
memory.events.local 同上,但仅统计当前 cgroup(不包含子 cgroup)。

这里我们使用 tuned 这个服务,这是一个系统调优的服务,作为一个独立的 service unit 存在,所以会有一个单独 Cgroup 分组

1
2
3
4
5
6
7
8
9
10
11
12
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$pgrep tuned
3654
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system]
└─$ps -o cgroup 3654
CGROUP
11:pids:/system.slice/tuned.service,
8:memory:/system.slice/tuned.service,
2:devices:/system.slice/tuned.service,
1:name=systemd:/system.slice/tuned.service
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system]
└─$

可以在 Cgroup 路径下面看到所有的指标数据 /sys/fs/cgroup/memory/system.slice/tuned.service/,还有部分内核相关的,这里我们只看一下内存相关的

1
2
3
4
5
6
7
8
9
10
11
12
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /sys/fs/cgroup/memory/system.slice/tuned.service/
cgroup.clone_children memory.kmem.limit_in_bytes memory.max_usage_in_bytes memory.pressure_level
cgroup.event_control memory.kmem.max_usage_in_bytes memory.memfs_files_info memory.qos_level
cgroup.kill memory.kmem.slabinfo memory.memsw.failcnt memory.soft_limit_in_bytes
cgroup.procs memory.kmem.tcp.failcnt memory.memsw.limit_in_bytes memory.stat
memory.events memory.kmem.tcp.limit_in_bytes memory.memsw.max_usage_in_bytes memory.swappiness
memory.events.local memory.kmem.tcp.max_usage_in_bytes memory.memsw.usage_in_bytes memory.usage_in_bytes
memory.failcnt memory.kmem.tcp.usage_in_bytes memory.min memory.use_hierarchy
memory.force_empty memory.kmem.usage_in_bytes memory.move_charge_at_immigrate notify_on_release
memory.high memory.limit_in_bytes memory.numa_stat tasks
memory.kmem.failcnt memory.low memory.oom_control

​​memory.usage_in_bytes​ : 当前 cgroup 中所有进程实际使用的物理内存总量(包括 RSS 和 Page Cache),单位字节(约 15.86 MB)。

1
2
3
4
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /sys/fs/cgroup/memory/system.slice/tuned.service/memory.usage_in_bytes
16629760

​​memory.memsw.usage_in_bytes​ : 当前 cgroup 中所有进程使用的物理内存 + Swap 空间的总量(单位字节)。此处与物理内存相等,说明未使用 Swap。

1
2
3
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /sys/fs/cgroup/memory/system.slice/tuned.service/memory.memsw.usage_in_bytes
16629760

memory.failcnt 存使用达到 memory.limit_in_bytes 设定的限制值的次数。值为 0 表示未触发过内存超限。

1
2
3
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /sys/fs/cgroup/memory/system.slice/tuned.service/memory.failcnt
0

memory.events 内存事件计数器:

  • low: 低内存压力事件次数
  • high: 高内存压力事件次数
  • limit_in_bytes: 达到内存限制的次数
  • oom: OOM(内存耗尽)触发次数。全为 0 表示无相关事件发生。
1
2
3
4
5
6
7
8
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /sys/fs/cgroup/memory/system.slice/tuned.service/memory.events
low 0
high 0
limit_in_bytes 0
oom 0
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system]
└─$

当然还有一些其他的参数,感兴小伙伴可以研究下,通过去读上面的参数可以进行性能分析,系统监控

proc 内存伪文件系统

如果需要详细查看一个进程使用了哪些虚拟地址,可用使用 pmap PID 命令或者基于 proc 内存伪文件系统查看内存详细信息,比如 /proc/1/status,/proc/PID/maps/proc/PID/smaps等,

查看进程详细内存段数据

pmap 1002

快速查看进程的虚拟内存布局和总占用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──[root@liruilongs.github.io]-[~]
└─$pmap 1002
1002: /usr/bin/python3 -Es /usr/sbin/tuned -l -P
0000561ae3586000 4K r---- python3.9 # 文件路径
0000561ae3587000 4K r-x-- python3.9
0000561ae3588000 4K r---- python3.9
0000561ae3589000 4K r---- python3.9
。。。。。。。。。。。。。。。。
00007ffc6f79b000 132K rw--- [ stack ] #进程栈。
00007ffc6f7ce000 16K r---- [ anon ] #匿名内存(堆、栈等)
00007ffc6f7d2000 8K r-x-- [ anon ]
ffffffffff600000 4K --x-- [ anon ]
total 256364K
┌──[root@liruilongs.github.io]-[~]
└─$

对应的列分别表示:内存段的起始虚拟地址(如 0000561ae3586000)。内存段的大小(如 4K)。内存访问权限 (r)可读 |(w)可写 |(x)可执行 |(s)共享 |(p)私有

/proc/1/maps

详细列出所有内存段的地址范围、权限和映射文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──[root@liruilongs.github.io]-[~]
└─$cat /proc/1/maps | head -20
55e05a46b000-55e05a471000 r--p 00000000 fd:00 372298 /usr/lib/systemd/systemd
55e05a471000-55e05a47c000 r-xp 00006000 fd:00 372298 /usr/lib/systemd/systemd
55e05a47c000-55e05a481000 r--p 00011000 fd:00 372298 /usr/lib/systemd/systemd
55e05a482000-55e05a483000 r--p 00016000 fd:00 372298 /usr/lib/systemd/systemd
55e05a483000-55e05a484000 rw-p 00017000 fd:00 372298 /usr/lib/systemd/systemd
55e05af99000-55e05b2a3000 rw-p 00000000 00:00 0 [heap]
7ff43c000000-7ff43c021000 rw-p 00000000 00:00 0
7ff43c021000-7ff440000000 ---p 00000000 00:00 0
7ff444000000-7ff444021000 rw-p 00000000 00:00 0
7ff444021000-7ff448000000 ---p 00000000 00:00 0
7ff44b761000-7ff44b762000 ---p 00000000 00:00 0
7ff44b762000-7ff44bf62000 rw-p 00000000 00:00 0
7ff44bf62000-7ff44bf63000 ---p 00000000 00:00 0
7ff44bf63000-7ff44c766000 rw-p 00000000 00:00 0
7ff44c766000-7ff44c768000 r--p 00000000 fd:00 33948825 /usr/lib64/libffi.so.8.1.0
7ff44c768000-7ff44c76e000 r-xp 00002000 fd:00 33948825 /usr/lib64/libffi.so.8.1.0
7ff44c76e000-7ff44c76f000 r--p 00008000 fd:00 33948825 /usr/lib64/libffi.so.8.1.0
7ff44c76f000-7ff44c770000 ---p 00009000 fd:00 33948825 /usr/lib64/libffi.so.8.1.0
7ff44c770000-7ff44c771000 r--p 00009000 fd:00 33948825 /usr/lib64/libffi.so.8.1.0
7ff44c771000-7ff44c772000 rw-p 0000a000 fd:00 33948825 /usr/lib64/libffi.so.8.1.0

可以看到在上面的基础上,展示了内存段的起始和结束虚拟地址(如 55e05a46b000-55e05a471000)。多了映射文件在文件中的偏移量(十六进制,如 00000000)。文件所在设备的编号(格式 fd:00,主设备号:次设备号)。文件的 inode 编号(如 372298)

/proc/1/smaps

提供每个内存段的详细物理内存统计(RSS、PSS、共享/私有内存等)

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
┌──[root@liruilongs.github.io]-[~]
└─$cat /proc/1/smaps | head -30
55e05a46b000-55e05a471000 r--p 00000000 fd:00 372298 /usr/lib/systemd/systemd
Size: 24 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 24 kB
Pss: 7 kB
Shared_Clean: 24 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 24 kB
Anonymous: 0 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 0
VmFlags: rd mr mw me sd
55e05a471000-55e05a47c000 r-xp 00006000 fd:00 372298 /usr/lib/systemd/systemd
Size: 44 kB
KernelPageSize: 4 kB
┌──[root@liruilongs.github.io]-[~]
└─$

部分字段说明​​:

  • Size​:​ 内存段的虚拟大小(如 24 kB)。
  • ​​Rss:(Resident Set Size)​​ 实际占用的物理内存(包含共享内存)。
  • ​​Pss:(Proportional Set Size)​​ 按共享比例计算的物理内存(如 3 个进程共享 24KB → 每个进程 Pss 8KB)。
  • ​​Shared_Clean/Shared_Dirty:​​ 共享内存中未修改/已修改的部分。
  • ​​Private_Clean/Private_Dirty:​​ 私有内存中未修改/已修改的部分。
  • ​​Swap​:​ 被交换到磁盘的内存大小。
  • ​​VmFlags​:​ 内存段的属性标志(如 rd 可读,mr 可映射,mw 可写等)。

进程全局内存数据统计

status 用于展示当前进程的一些基本指标,进程基础信息,权限与身份​​,信号与中断​等

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /proc/1/status
Name: systemd
Umask: 0000
State: S (sleeping)
Tgid: 1
Ngid: 0
Pid: 1
PPid: 0
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 256
Groups:
NStgid: 1
NSpid: 1
NSpgid: 1
NSsid: 1
VmPeak: 165112 kB
VmSize: 100636 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 12688 kB
VmRSS: 12688 kB
RssAnon: 4084 kB
RssFile: 8604 kB
RssShmem: 0 kB
VmData: 19052 kB
VmStk: 1036 kB
VmExe: 884 kB
VmLib: 8832 kB
VmPTE: 84 kB
VmSwap: 0 kB
HugetlbPages: 0 kB
CoreDumping: 0
THP_enabled: 1
Threads: 1
SigQ: 0/29616
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 7be3c0fe28014a03
SigIgn: 0000000000001000
SigCgt: 00000001000004ec
CapInh: 0000000000000000
CapPrm: 000001ffffffffff
CapEff: 000001ffffffffff
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp: 0
Seccomp_filters: 0
Speculation_Store_Bypass: thread vulnerable
Cpus_allowed: f
Cpus_allowed_list: 0-3
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 2558
nonvoluntary_ctxt_switches: 483

这里只关注内存相关的

虚拟内存核心指标

参数 描述
VmPeak 165,112 kB 进程生命周期内虚拟内存的 峰值(含已分配但未使用的内存),反映进程曾达到的最大内存需求
VmSize 100,636 kB 当前进程 虚拟地址空间总大小(包括代码、数据、堆、栈等所有映射区域),约 98.27 MB

物理内存核心指标

参数 描述
VmHWM 12,688 kB 进程物理内存使用 峰值(Resident Set Size 最大值),约 12.38 MB
VmRSS 12,688 kB 当前实际驻留物理内存(RSS),等于 RssAnon + RssFile + RssShmem,约 12.38 MB
RssAnon 4,084 kB 匿名页(动态分配的堆/栈内存)占用的物理内存,例如 malloc 分配的未映射文件的内存
RssFile 8,604 kB 文件页缓存占用的物理内存(如加载的共享库、内存映射文件)
RssShmem 0 kB 共享内存段(如 shmget 创建的 IPC 内存)占用的物理内存

内存区域细分指标

参数 描述
VmData 19,052 kB 数据段 + 堆 的虚拟内存大小(动态分配的内存通过 brkmmap 扩展)
VmStk 1,036 kB 栈空间 的虚拟内存大小(存放局部变量和函数调用帧),默认上限由 ulimit -s 控制
VmExe 884 kB 可执行代码段 的虚拟内存大小(程序本身的机器指令,只读)
VmLib 8,832 kB 共享库 的虚拟内存大小(如 glibc 等动态链接库)

其他关键参数

参数 描述
VmLck 0 kB 锁定的物理内存(通过 mlock 系统调用防止被换出到 Swap),常用于实时性要求高的场景
VmPTE 84 kB 页表项 占用的物理内存(用于管理虚拟地址到物理地址的映射关系)
VmSwap 0 kB 当前已换出到 Swap 分区 的内存大小(若值持续增长,需排查内存泄漏或物理内存不足)

关键应用场景分析

内存泄漏检测

  • 对比 VmSizeVmRSS:若 VmSize 持续增长而 VmRSS 稳定,可能为虚拟内存分配过多但未实际使用(如未初始化的 malloc)。
  • 监控 VmSwap:若长期非零,需检查物理内存是否不足或进程存在内存滥用。

性能优化方向

  • 共享库优化VmLib 值较高时,可考虑静态链接或减少动态库依赖以降低内存开销。
  • 堆栈管理VmDataVmStk 异常增长可能提示堆内存泄漏或递归调用过深。

系统资源分配

  • VmHWM 可用于设置 CGroup 内存限制(memory.max_usage_in_bytes),避免单个进程耗尽物理内存。
  • RssFile 较高时,可通过清理缓存(sync; echo 1 > /proc/sys/vm/drop_caches)释放非关键文件缓存。

statm 用于展示进程内存快照,但需注意 单位是内存页,通常 1 页 = 4 KB

1
2
3
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /proc/1/statm
25159 3172 2151 221 0 5022 0

字段解析

参数 描述 换算为 KB
size 25159 进程虚拟地址空间总大小(含代码、数据、堆栈等所有映射区域) 25159 × 4 ≈ 100,636 KB
Resident 3172 实际驻留物理内存(RSS),即当前进程使用的物理内存总量 3172 × 4 ≈ 12,688 KB
Shared 2151 共享内存页数(如动态链接库、共享内存段等被多个进程共享的部分) 2151 × 4 ≈ 8,604 KB
Trs 221 可执行代码段(Text Resident Set)占用的内存页(如程序自身的机器指令) 221 × 4 ≈ 884 KB
Lrs 0 库的内存页数(Linux 2.6+ 中已废弃,通常为 0) -
Drs 5022 数据段(堆、全局变量)和用户态栈的总内存页 5022 × 4 ≈ 20,088 KB
dt 0 脏页数量(已修改但未写入磁盘的页,Linux 2.6+ 中已废弃) -

/proc/1/status 的关联 对比 /proc/1/status 中的内存参数可验证数据一致性

VmSize: 100636 kB = size × 4 = 25159 × 4
VmRSS: 12688 kB = Resident × 4 = 3172 × 4
RssFile: 8604 kBShared × 4 = 2151 × 4
VmExe: 884 kB = Trs × 4 = 221 × 4
VmData + VmStk: 19052 + 1036 = 20088 kBDrs × 4 = 5022 × 4

smaps_rollup 提供进程 ​​全局汇总统计​​(如总 RSS、PSS、Swap 等), 读取 smaps_rollup 比遍历 smaps 更高效(减少锁竞争时间),适合高频监控场景,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /proc/1/smaps_rollup
56476fafe000-7ffc5b7f8000 ---p 00000000 00:00 0 [rollup]
Rss: 12692 kB
Pss: 4648 kB
Pss_Anon: 3463 kB
Pss_File: 1184 kB
Pss_Shmem: 0 kB
Shared_Clean: 8540 kB
Shared_Dirty: 936 kB
Private_Clean: 64 kB
Private_Dirty: 3152 kB
Referenced: 12692 kB
Anonymous: 4088 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB

核心内存统计

参数 描述
Rss 12,692 kB 常驻物理内存总量(包含共享和私有内存),等于 Shared_Clean + Shared_Dirty + Private_Clean + Private_Dirty
Pss 4,648 kB 比例集内存(按共享比例分摊后的内存),Pss = Pss_Anon + Pss_File + Pss_Shmem
Pss_Anon 3,463 kB 匿名页(如堆、栈)分摊后的内存,反映独占或部分共享的匿名内存
Pss_File 1,184 kB 文件页缓存(如共享库、映射文件)分摊后的内存
Pss_Shmem 0 kB 共享内存(如 tmpfs)的分摊内存,此处未使用

共享与私有内存分布

参数 描述
Shared_Clean 8,540 kB 共享的未修改内存(如只读共享库),可被内核直接回收
Shared_Dirty 936 kB 共享的已修改内存(如被多个进程写入的共享内存),需同步到磁盘后才能回收
Private_Clean 64 kB 私有的未修改内存(如未修改的私有数据),可快速回收
Private_Dirty 3,152 kB 私有的已修改内存(如进程堆内存),需写入 Swap 或文件后才能回收

其他关键参数

参数 描述
Anonymous 4,088 kB 匿名内存总量(无法关联文件的内存,如 malloc 分配的内存)
Swap 0 kB 已换出到 Swap 分区的内存量,此处为 0 表示未启用 Swap 或内存充足
Locked 0 kB 通过 mlock 锁定的内存(不可被换出),常用于实时性要求高的场景
Referenced 12,692 kB 最近被访问过的内存页,反映当前活跃内存

这是两个 OOM 内存杀手相关的,这里不多讲,在之后的博客中会和小伙伴分享

1
2
3
4
5
6
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system] 
└─$cat /proc/1/oom_score
0
┌──[root@liruilongs.github.io]-[/usr/lib/systemd/system]
└─$cat /proc/1/oom_score_adj
0

博文部分内容参考

© 文中涉及参考链接内容版权归原作者所有,如有侵权请告知 :)


《 Red Hat Performance Tuning 442 》

《性能之巅 系统、企业与云可观测性(第2版)》


© 2018-至今 liruilonger@gmail.com, 保持署名-非商用-相同方式共享(CC BY-NC-SA 4.0)

发布于

2025-04-11

更新于

2025-04-14

许可协议

评论
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×