Linux内核给每个进程提供了一个独立的连续虚拟地址空间( 注意独立
代表: 可以将进程内存隔离)。
每个进程的虚拟地址分为内核空间和用户空间。
但内核空间,其实关联的都是相同的物理内存。
内核态可以访问内核空间内存。
进程用户态只能访问用户空间内存;
1. 内存映射
1.1. 什么是内存映射:
内存映射就是将虚拟内存地址映射到物理内存地址,内核为每个进程都维护了一张页表,记录映射关系。
1.2 页表的概念
页表实际存储在CPU的内存管理单元MMU中,页表还有一个缓冲TLB(Translation Lookaside Buffer,转译后备缓冲器)。
页的大小为4KB,为了解决页过多,
Linux提供了两种机制多级页表和大页(HugePage)。
- 多级页表: 就是把内存分成区块来管理,将原来的映射关系改成区块索引和区块内的偏移,这样可以大大减少页表的项数。(其实也可以直接映射正使用的页,但这会导致页不连续,不能使用偏移量查找)
Linux使用四级页表管理内存页。
- 大页:就是比普通页更大的内存块。
当访问虚拟地址在页表中找不到时,会产生缺页异常,进入内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。
2. 虚拟内存空间分布
上图为32位系统虚拟内存空间分布。
除内核空间外,
用户空间内存,从顶层到底分别为:
- 栈,包括局部变量和函数调用的上下文等。栈大小是固定的,一般是 8MB。
- 文件映射段,包括动态库、共享内存等,从高地址开始向下增长。
- 堆,包括动态分配的内存,从低地址向上增长。
- 数据段,包括全局变量等。
- 只读段,包括代码和常量等。
使用 C 标准库的 malloc() 或者 mmap() ,就可以分别在堆和文件映射段动态分配内存。
3. 内存分配与回收
3.1 内存分配
C 标准库采用 malloc() 分配内存,有两种实现方式,brk
和 mmap()
。
-
brk() 分配小内存(小于 128K),通过移动堆顶的位置实现,释放后不会立刻归还内存,而是被缓存起来,供重复使用。
-
mmap() 分配大内存(大于 128K),在文件映射段找一块空闲内存分配出去,释放后直接归还系统,所以每次都会发生缺页异常。
在内核空间,Linux 则通过 slab
分配器来管理小内存。可以把 slab
看成构建在伙伴系统上的一个缓存,主要作用就是分配并释放内核中的小对象。
对于用户空间,malloc 通过 brk() 分配;
malloc()
申请内存后,内存并不会立即分配,而是在首次访问时,才通过缺页异常陷入内核中分配内存。
3.2 内存回收
当内存紧张时,系统会通过一系列机制回收内存:
- 回收缓存,如 LRU 算法;
- 回收不常访问的内存,把不常使用的内存通过交换分区直接写到磁盘;
- 杀死进程,通过 OOM(Out of Memory)直接杀死占用大量内存的进程。
4. 查看内存
4.1 查看系统内存 free
total:总内存大小;
used:已使用内存的大小,包含了共享内存;
free:未使用内存的大小;
shared:共享内存的大小;
buff/cache:缓存和缓存区的大小;
available:新进程可用内存的大小,包含未使用内存和可回收的缓存。
$ free
total used free shared buff/cache available
Mem: 16254236 6001652 3964020 349384 6288564 9565504
Swap: 2097148 768 2096380
4.2 查看进程内存 top
- VIRT:进程虚拟内存大小,包括进程申请过的内存,即便没有分配;
- RES:常驻内存大小,即实际使用的物理内存大小,不包括 Swap 和共享内存;
- SHR:共享内存大小,如与其它进程共同使用的共享内存、加载的动态链接库和程序代码段等;
- %MEM:进程使用的物理内存占系统内存的百分比。
4.3 Buffer 和Cache
两个角度 理解: Buffer 和Cache
角度一: man free
Buffer :是内核缓冲区用到的内存,对应的是 /proc/meminfo
中的 Buffers
值。
Cache: 是内核页缓存和 Slab 用到的内存,对应的是 /proc/meminfo
中的 Cached
与 SReclaimable
之和。
角度二: man proc
, 关于 meminfo
说明
-
Buffers 是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据,通常不会特别大(20MB 左右)。
-
Cached 是从磁盘读取文件的页缓存,也就是用来缓存从文件读取的数据。
Slab 包括两部分,SReclaimable 和 SUnreclaim 。
其中的可回收部分,用 SReclaimable 记录;
而不可回收部分,用 SUnreclaim 记录。
- 磁盘和文件写
# 清理文件页、目录项、Inodes 等各种缓存
$ echo 3 > /proc/sys/vm/drop_caches
读取随机设备,生成一个 50MB 大小的文件:
$ dd if=/dev/urandom of=/tmp/file bs=1M count=50
-
磁盘和文件的读: 结果同磁盘和文件写。
-
vmstat 1
查看,cache 增长,buff 几乎不变。
$ 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 1280 4392104 183452 5940636 0 0 68 22 313 118 6 7 87 0 0
0 0 1280 4385552 183452 5940776 0 0 0 552 3795 8138 2 1 96 0 0
0 0 1280 4385552 183452 5939872 0 0 0 0 3770 7785 1 1 97 0 0
5. 内存分析指标
- 内存分析工具
-
内存分析流程