前些时间在工作中遇到设备上某个进行占用虚拟内存过大,需要对其进行分析定位。而进程中使用虚拟内存最多的地方主要有malloc申请未释放,定义全局量使用,链接开源库。
下面以xx_event_agent进程进行分析,首先ps查看该进程id为280。
/proc/pid/status
root@WDFG201-D:/proc/280 # cat status
Name: xx_event_agent
Umask: 0022
State: S (sleeping)
Tgid: 280
Ngid: 0
Pid: 280
PPid: 1
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 32
Groups:
VmPeak: 58688 kB
VmSize: 58652 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 2892 kB
VmRSS: 2888 kB
RssAnon: 252 kB
RssFile: 2544 kB
RssShmem: 92 kB
VmData: 50056 kB
VmStk: 132 kB
VmExe: 88 kB
VmLib: 3024 kB
VmPTE: 26 kB
VmPMD: 0 kB
VmSwap: 0 kB
Threads: 7
SigQ: 0/3918
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000006
SigCgt: 0000000180000000
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Speculation_Store_Bypass: unknown
Cpus_allowed: 3
Cpus_allowed_list: 0-1
voluntary_ctxt_switches: 32
nonvoluntary_ctxt_switches: 8
关于内存相关的参数,含义如下:
/proc/pid/maps
root@WDFG201-D:/proc/280 # cat maps
00010000-00026000 r-xp 00000000 1f:05 1388 /sbin/gc_event_agent
00035000-00036000 r--p 00015000 1f:05 1388 /sbin/gc_event_agent
00036000-00037000 rw-p 00016000 1f:05 1388 /sbin/gc_event_agent
00037000-00058000 rw-p 00000000 00:00 0 [heap]
b3600000-b3621000 rw-p 00000000 00:00 0
b3621000-b3700000 ---p 00000000 00:00 0
b3700000-b3721000 rw-p 00000000 00:00 0
b3721000-b3800000 ---p 00000000 00:00 0
b3800000-b3a00000 r--s 00000000 00:05 32769 /SYSV610d07d2 (deleted) ///共享区,一般由mmap映射
b3a00000-b3a21000 rw-p 00000000 00:00 0
b3a21000-b3b00000 ---p 00000000 00:00 0
b3b67000-b3b68000 ---p 00000000 00:00 0
b3b68000-b4368000 rw-p 00000000 00:00 0
b4368000-b4369000 ---p 00000000 00:00 0
b4369000-b4b69000 rw-p 00000000 00:00 0
b4b69000-b4b6a000 ---p 00000000 00:00 0
b4b6a000-b536a000 rw-p 00000000 00:00 0
b536a000-b536b000 ---p 00000000 00:00 0
b536b000-b5b6b000 rw-p 00000000 00:00 0
b5b6b000-b5b6c000 ---p 00000000 00:00 0
b5b6c000-b636c000 rw-p 00000000 00:00 0
b636c000-b636d000 ---p 00000000 00:00 0
b636d000-b6b6d000 rw-p 00000000 00:00 0
b6b6d000-b6c9a000 r-xp 00000000 1f:05 396 /lib/libc-2.29.so
b6c9a000-b6caa000 ---p 0012d000 1f:05 396 /lib/libc-2.29.so
b6caa000-b6cac000 r--p 0012d000 1f:05 396 /lib/libc-2.29.so
b6cac000-b6cad000 rw-p 0012f000 1f:05 396 /lib/libc-2.29.so
b6cad000-b6cb0000 rw-p 00000000 00:00 0 //没有文件名的条目,实际上是上一个有文件名的.bss段映射,它们并不在文件中占用空间,需要在加载时清零。所以做文件映射就不能实现该功能(因为文件里没有对应的内容),所以这里其实是映射到了一个匿名的私有空间,逻辑地址空间和前面的连续。
b6cb0000-b6ccd000 r-xp 00000000 1f:05 467 /lib/libgcsutils.so
b6ccd000-b6cdc000 ---p 0001d000 1f:05 467 /lib/libgcsutils.so
b6cdc000-b6cdd000 r--p 0001c000 1f:05 467 /lib/libgcsutils.so
b6cdd000-b6cde000 rw-p 0001d000 1f:05 467 /lib/libgcsutils.so
b6cde000-b6ce9000 r-xp 00000000 1f:05 466 /lib/libgcsmsg.so
b6ce9000-b6cf9000 ---p 0000b000 1f:05 466 /lib/libgcsmsg.so
b6cf9000-b6cfa000 r--p 0000b000 1f:05 466 /lib/libgcsmsg.so
b6cfa000-b6cfb000 rw-p 0000c000 1f:05 466 /lib/libgcsmsg.so
b6cfb000-b6cfc000 r-xp 00000000 1f:05 498 /lib/liblog.so
b6cfc000-b6d0b000 ---p 00001000 1f:05 498 /lib/liblog.so
b6d0b000-b6d0c000 r--p 00000000 1f:05 498 /lib/liblog.so
b6d0c000-b6d0d000 rw-p 00001000 1f:05 498 /lib/liblog.so
b6d0d000-b6d57000 r-xp 00000000 1f:05 504 /lib/libmdm.so
b6d57000-b6d67000 ---p 0004a000 1f:05 504 /lib/libmdm.so
b6d67000-b6d68000 r--p 0004a000 1f:05 504 /lib/libmdm.so
b6d68000-b6da3000 rw-p 0004b000 1f:05 504 /lib/libmdm.so
b6da3000-b6db3000 rw-p 00000000 00:00 0
b6db3000-b6e43000 r-xp 00000000 1f:05 502 /lib/libm-2.29.so
b6e43000-b6e52000 ---p 00090000 1f:05 502 /lib/libm-2.29.so
b6e52000-b6e53000 r--p 0008f000 1f:05 502 /lib/libm-2.29.so
b6e53000-b6e54000 rw-p 00090000 1f:05 502 /lib/libm-2.29.so
b6e54000-b6e7d000 r-xp 00000000 1f:05 462 /lib/libgc.so
b6e7d000-b6e8c000 ---p 00029000 1f:05 462 /lib/libgc.so
b6e8c000-b6e8d000 r--p 00028000 1f:05 462 /lib/libgc.so
b6e8d000-b6e8e000 rw-p 00029000 1f:05 462 /lib/libgc.so
b6e8e000-b6ef2000 r-xp 00000000 1f:05 426 /lib/libevent-2.1.so.7.0.0
b6ef2000-b6f01000 ---p 00064000 1f:05 426 /lib/libevent-2.1.so.7.0.0
b6f01000-b6f02000 r--p 00063000 1f:05 426 /lib/libevent-2.1.so.7.0.0
b6f02000-b6f03000 rw-p 00064000 1f:05 426 /lib/libevent-2.1.so.7.0.0
b6f03000-b6f19000 r-xp 00000000 1f:05 586 /lib/libpthread-2.29.so
b6f19000-b6f28000 ---p 00016000 1f:05 586 /lib/libpthread-2.29.so
b6f28000-b6f29000 r--p 00015000 1f:05 586 /lib/libpthread-2.29.so
b6f29000-b6f2a000 rw-p 00016000 1f:05 586 /lib/libpthread-2.29.so
b6f2a000-b6f2c000 rw-p 00000000 00:00 0
b6f2c000-b6f4c000 r-xp 00000000 1f:05 365 /lib/ld-2.29.so
b6f57000-b6f58000 rw-s 00000000 00:05 0 /SYSV000004d2 (deleted)
b6f58000-b6f5c000 rw-p 00000000 00:00 0
b6f5c000-b6f5d000 r--p 00020000 1f:05 365 /lib/ld-2.29.so
b6f5d000-b6f5e000 rw-p 00021000 1f:05 365 /lib/ld-2.29.so
beb13000-beb34000 rw-p 00000000 00:00 0 [stack]
becd0000-becd1000 r-xp 00000000 00:00 0 [sigpage]
ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors]
输出信息依次为:
第一列:本段在虚拟内存中的地址范围
第二列:本段的权限
第三列:偏移地址,即指本段映射地址在文件中的偏移
第四列:主设备号和次设备号
第五列:文件索引节点号
第六列:映射的文件名
例:00010000-00026000 r-xp 00000000 1f:05 1388 /sbin/gc_event_agent
00010000-00026000 :是该虚拟内存段的开始和结束位置
r-xp:内存段的权限,分别是可读、可写、可运行、私有或共享,最后一位p代表私有,s代表共享/r-xp:代码段;r--p:常量段;rw-p:全局变量;---p:两个LOAD区间中空闲的内存空间,为栈保护区,可以达到栈溢出保护的作用;
00000000:该虚拟内存段起始地址在对应的映射文件中以页为单位的偏移量,对匿名映射,它等于0或者vm_start/PAGE_SIZE
1f:05:文件的主设备号和次设备号。对匿名映射来说,因为没有文件在磁盘上,所以没有设备号,始终为00:00。对有名映射来说,是映射的文件所在设备的设备号。
1388:被映射到虚拟内存的文件的索引节点号,通过该节点可以找到对应的文件,对匿名映射来说,因为没有文件在磁盘上,所以没有节点号,始终为00:00。
/sbin/gc_event_agent:被映射到虚拟内存的文件名称。后面带(deleted)的是内存数据,可以被销毁。对有名来说,是映射的文件名。对匿名映射来说,是此段虚拟内存在进程中的角色。[stack]表示在进程中作为栈使用,[heap]表示堆。其余情况则无显示。
/proc/pid/smaps
/proc/pid/smaps文件是基于/proc/pid/maps的扩展,它展示了一个进程的内存消耗,比同一个目录下的maps文件更为详细。
root@WDFG201-D:/proc/274 # cat smaps
00010000-00026000 r-xp 00000000 1f:04 1546 /sbin/gc_event_agent
Size: 88 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 88 kB
Pss: 88 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 88 kB
Private_Dirty: 0 kB
Referenced: 88 kB
Anonymous: 0 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
VmFlags: rd ex mr mw me dw
00035000-00036000 r--p 00015000 1f:04 1546 /sbin/gc_event_agent
Size: 4 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 4 kB
Pss: 4 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 4 kB
Referenced: 4 kB
Anonymous: 4 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
VmFlags: rd mr mw me dw ac
00036000-00037000 rw-p 00016000 1f:04 1546 /sbin/gc_event_agent
Size: 4 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 4 kB
Pss: 4 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 4 kB
Referenced: 4 kB
Anonymous: 4 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
VmFlags: rd wr mr mw me dw ac
Size:虚拟内存空间大小。但是这个内存值不一定是物理内存实际分配的大小,因为在用户态上,虚拟内存总是延迟分配的。这个值计算也非常简单,就是该VMA的开始位置减结束位置。
Rss:是实际分配的内存,这部分物理内存已经分配,不需要缺页中断就可以使用的。
Pss(proportional set size):是平摊计算后的实际物理使用内存(有些内存会和其他进程共享,例如mmap进来的)。
Referenced:当前页面被标记为已引用或者包含匿名映射(The amount of memory currently marked as referenced or a mapping associated with a file may contain anonymous pages)。
Anonymous:匿名映射的物理内存,这部分内存不来自于文件的内存大小。
ShmemPmdMapped:PMD页面已经被映射的共享(shmem / tmpfs)内存量。在官方文档中,这样解释:"ShmemPmdMapped" shows the ammount of shared (shmem/tmpfs) memory backed by huge pages.
Shared/Private_Hugetlb:由hugetlbfs页面支持的内存使用量,由于历史原因,该页面未计入“ RSS”或“ PSS”字段中。 并且这些没有包含在Shared/Private_Clean/Dirty 字段中。
Swap:存在于交换分区的数据大小(如果物理内存有限,可能存在一部分在主存一部分在交换分区)
SwapPss:这个我并没有找到对应解释,但从源码可以得知,计算逻辑就跟pss一样,只不过针对的是交换分区的内存。
KernelPageSize:内核一页的大小
MMUPageSize:MMU页大小,大多数情况下,和KernelPageSize大小一样。
Locked:常驻物理内存的大小,这些页不会被换出。
THPeligible:映射是否符合分配THP的条件。如果为true,则为1,否则为0。 它仅显示当前状态。
THP,透明大页(Transparent Huge Pages),RHEL 6 开始引入,目的是使用更大的内存页面(memory page size) 以适应越来越大的系统内存,让操作系统可以支持现代硬件架构的大页面容量功能。与标准大页的区别在于分配机制,标准大页管理是预分配的方式,而透明大页管理则是动态分配的方式。
VmFlags:表示与特定虚拟内存区域关联的内核标志
注释:
段名 | 存储属性 | 内存分配 |
代码段 | 存放可执行程序的指令,存储态和运行态都有 | 静态 |
数据段 | 存放已初始化(非零初始化的全局变量和静态局部变量)的数据,存储态和运行态都有 | 静态 |
bss段 | 存放未初始化(未初始化或者0初始化的全局变量和静态局部变量)的数据,存储态和运行态都有 | 静态 |
堆 | 动态分配内存,需要通过malloc手动申请,free手动释放,适合大块内存。容易造成内存泄漏和内存碎片。运行态才有。 | 动态 |
栈 | 存放函数局部变量和参数以及返回值,函数返回后,由操作系统立即回收。栈空间不大,使用不当容易栈溢出。运行态才有 | 静态 |