这篇文章上次修改于 899 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
1 内存映射
每个进程都有独立的虚拟空间,虚拟空间包含了用户空间和内核空间,不同进程的内核空间关联的都是相同的物理内存。
内存映射,其实就是将虚拟地址映射为物理内存地址。内核为每个进程都维护了一张页表,记录虚拟地址与物理地址的映射关系。
页表位于内存管理单元 MMU 中,正常情况下,处理器可通过硬件访问内存。当虚拟地址没有在页表中查到时,会产生缺页中断,进入内核空间分配内存,更新页表,然后回到用户空间,恢复进程运行。
TLB 是页表的高速缓存。
内存映射的最小单位是页,通常是 4KB 或者 4KB 的整数倍。
为了解决页表项过多的问题,Linux 引入了两种解决方式:多级页表和大页。
2 虚拟内存空间分布
- 只读段:存放代码和常量等。
- 数据段:包括全局变量和静态变量等。
- 堆:包括动态分配的内存,从低地址向上增长。使用 malloc() 分配。
- 文件映射:包括动态库,共享内存等,从高地址向下增长。使用 mmap() 分配。
- 栈:包括局部变量和函数调用上下文,固定大小,一般是 8M。
3 内存分配与回收
malloc() 是 C 库提供的函数,对应两种系统调用,brk() 和 mmap()。
brk() 用于分配小于 128K 的内存。内存释放后不会不会立刻归还系统,而是缓存起来,提供内存访问效率,但是频繁的分配和释放会造成内存碎片。
mmap() 用于分配大于 128K 的内存,在文件映射区分配。内存释放后会立刻归还系统,频繁的内存分配会导致大量缺页异常。
以上两种系统调用发生后,并不会真正分配内存,而是首次访问时通过缺页异常进入内核,由内核分配。
Linux 通过伙伴系统管理内存分配。伙伴系统以页为单位管理内存。
如果要比页小的内存,用户空间中,malloc() 通过 brk() 分配内存。内核空间中,Linux 通过 slab 内存分配器管理小内存。可将 slab 视为伙伴系统的缓存。
当内存紧张时,会通过如下方式回收内存:
- 回收缓存,比如通过 LRU 算法回收最近最少使用的内存页面。
- 回收不常访问的内存,通过交换分区直接写到磁盘。读磁盘速度远慢于读内存速度,当内存不足时使用该方式。
杀死进程,通过 OOM 杀死占用内存大的进程。
- 进程占用的内存越大,oom_score 越大。
- 进程占用的 CPU 越多,oom_score 越小。
- oom_score 越大,约容易被 OOM 杀死。
4 如何查看内存使用情况
# 注意不同版本的free输出可能会有所不同
$ free
total used free shared buff/cache available
Mem: 8169348 263524 6875352 668 1030472 7611064
Swap: 0 0 0
- total:总内存大小。
- used:已使用内存大小。
- free:未使用内存大小。
- shared:共享内存大小。
buff/cache:缓存区和缓冲区大小。
buffer 是对磁盘数据的缓存。
- 磁盘数据是指块设备文件。读写块设备文件时,会跳过与文件系统,直接与磁盘交互,即“裸 I/O“。
cache 是对文件数据的缓存。
- 文件是指普通的文件。读写普通文件时,I/O 请求会经过文件系统,由文件系统负责与磁盘的交互。
- available:新进程的可用内存大小。包括未使用内存和可回收缓存。
# 按下M切换到内存排序
$ top
...
KiB Mem : 8169348 total, 6871440 free, 267096 used, 1030812 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 7607492 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
430 root 19 -1 122360 35588 23748 S 0.0 0.4 0:32.17 systemd-journal
1075 root 20 0 771860 22744 11368 S 0.0 0.3 0:38.89 snapd
1048 root 20 0 170904 17292 9488 S 0.0 0.2 0:00.24 networkd-dispat
1 root 20 0 78020 9156 6644 S 0.0 0.1 0:22.92 systemd
12376 azure 20 0 76632 7456 6420 S 0.0 0.1 0:00.01 systemd
12374 root 20 0 107984 7312 6304 S 0.0 0.1 0:00.00 sshd
...
- VIRT:虚拟内存,包括已申请未分配的内存。
- RES:常驻内存,指已经分配的内存。
- SHR:共享内存,包括动态库,程序代码段和与其他进程共同使用的共享内存等。
- MEM:占用内存比例。
# 每隔1秒输出1组数据
$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 7743608 1112 92168 0 0 0 0 52 152 0 1 100 0 0
0 0 0 7743608 1112 92168 0 0 0 0 36 92 0 0 100 0 0
- buff 和 cache:单位为 KB
- bi 和 bo:分别表示块设备的读取和写入大小,单位为 KB/s
参考
倪朋飞. Linux 性能优化实战.
没有评论