1. 进程的 VMA
(1) 进程地址空间
在 Linux 系统中,每个进程都有自己的虚拟内存空间 0~3G;
内核空间只有一个 3G~4G;
进程通过系统 API 调用,在内核空间申请内存,不统计在任何用户进程;进程消耗内存,单指用户空间内存消耗;
(2)VMA 列表
LINUX 用 task_struct 来描述进程,其中的 mm_struct 是描述内存的结构体,mm_struct 有一个 vma 列表,管理当前进程的所有 vma 段。
每个进程的内存由多个 vma 段组成:
(3) 查看 VMA 方法:
1.pmap
由图知,从接近 0 地址开始,第一个 4K 是只读代码段,第二个 4K 是只读数据段,还有其他共享库代码段,堆栈等;
可见一个进程的 VMA 涵盖多个地址区域 ,但并没有覆盖所有地址空间 。VMA 未覆盖的地址空间是 illegal 的,访问这些地址,缺页中断,发生 pagefault.
2.cat /proc/pid/maps
读文件形式,与 pmap 一一对应;
3.cat /proc/pid/smaps
更详细的描述
00400000-00401000 r-xp 00000000 08:15 20316320 /home/leon/work/linux/mm/a.out
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: 4 kB
Private_Dirty: 0 kB
Referenced: 4 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 sd
查看 VMA 的三个方法对比
VMA 的来源,代码段,数据段,堆栈段。
VMA 是 linux 最核心数据结构之一。
2.page fault 的几种可能性,major 和 minor
mmu 给 cpu 发 page fault 时,可以从寄存器读到两个元素,pagefault 地址 ,pagefault 原因 。
(1) 访问 Heap 堆 (首次申请,不是从 Libc 获取),第一次写,发生 pagefault,linux 检查 VMA 权限,发现权限合法,发缺页中断,申请一页内存,并更新页表。
(2) 访问空区域,访问非法,发段错误;
(3) 访问代码段, 在此区域写,报 pagefault,检查权限发现错误,报段错误;
(4) 访问代码段, 在此区域读 / 执行,linux 检查权限合法,若代码不在内存,那么申请内存页,把代码从硬盘读到内存。
伴随 I / O 的 pagefault, 叫 major pagefault, 否则 minor pagefault.
major pagefault 耗时远大于 minor pagefault.
\time -v python hello.py
3. 内存是如何被瓜分的::vss、rss、pss 和 uss
rss 是不是代表进程的内存消耗呢,NO。
VSS:单个进程全部可访问的地址空间,但未必所有虚拟地址都已经映射物理内存;
RSS:驻留内存,单个进程实际占用的物理内存大小 (不十分准确的描述);上图的进程 1
PSS:
比例化的内存消耗,相对 RSS,将共享内存空间按被共享进程数量比例化;上图的 C 库 4 被三个进程共享,所以 4 /3;
USS:进程独占内存,比如上图的堆 6。
案例,连续运行两次 a.out,查看内存占用情况
运行程序 a.out
./a.out &
pidof a.out
cat /proc/pid/smaps |more
./a.out &
pidof a.out
cat /proc/pid_2/smaps |more
PSS 变化
shared_clean
private_clean
4. 应用内存泄漏的界定方法
统计系统的内存消耗,查看 PSS。
检查有没有内存泄漏,检查 USS
./smem
smem –pie=command
smem –bar=command
android 里面有类似的工具,procmem/procrank
smem 分析系统内存使用是通过 /proc/smaps 的,procrank 是通过分析 /proc/kpagemap。
5. 应用内存泄漏的检测方法:valgrind 和 addresssanitizer
其他查询内存泄漏工具:
valgrind:
gcc -g leak-example.c -o leak-example
valgrind --tool=memcheck --leak-check=yes ./leak-example
==20468== HEAP SUMMARY:
==20468== in use at exit: 270,336 bytes in 22 blocks
==20468== total heap usage: 44 allocs, 22 frees, 292,864 bytes allocated
==20468==
==20468== 258,048 bytes in 21 blocks are definitely lost in loss record 2 of 2
==20468== at 0x4C2DB8F: malloc (in
/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==20468== by 0x4005C7: main (leak-example.c:6)
valgrid 是在虚拟机跑 APP,速度很慢;
新版 gcc4.9 以后集成了 asan
asan:
gcc -g -fsanitize=address ./leak-example.c
gcc -fuse-ld=gold -fsanitize=address -g ./lsan.c
#1 0x40084e in main lsan.c:9
内存泄漏不一定在用户空间,排查内核空间,检测 slab , vmalloc
slaptop, 检查申请和释放不成对。
在内核编译,打开 kmemleak 选项。
5. 工程调试内存泄漏问题一般步骤:
(1) meminfo, free 多点采样确认有内存泄漏。
(2) 定位,smem 检查用户空间
USS 在不断增加。
(3) slab, 检查内核空间。
cat /proc/slabinfo
其他查看内存信息的方法
cat /proc/meminfo
cat /proc/buddyinfo
cat /proc/zoneinfo
cat /proc/meminfo