1、内存映射

1、内存

大多数计算机用的主存都是动态随机访问内存(DEAM)。只有内核才能直接访问物理内存。
Linux内核给每个进程提供了一个独立额外虚拟地址空间,并且这个地址空间是连续的。
虚拟地址空间的内部又被分为内核空间用户空间两部分,不同子长(也就是单个CPU指令可以处理数据的最大长度)的处理器,地址空间范围也不同。如32位和64位系统,他们的虚拟地址空间
image.png
32位系统的内核空间占用1G,位于最高处,剩下的是3G的用户空间。
64位系统的内核空间和用户空间都是128T,分别位于最高和最低位,中间部分未定义。

2、内存映射

内存映射,就是将虚拟内存地址映射到物理内存地址。为了完成内存映射,每个进程都维护了一张页表,记录虚拟地址和物理地址的映射关系。
image.png
1、页表实际上存储在CPU的内存管理单元 MMU 中。正常情况下,处理器可以直接通过硬件,找到要访问的内存。
当进程访问的虚拟地址在页表中查不到时,系统会产生一个缺页异常,进入内核空间分配物理内存、更新进程页表,再返回用户空间,恢复进程的运行。
2、TLB(Translation Lookaside Buffer,转译后备缓冲器)会影响CPU的内存访问性能。
它是MMU中页表的高速缓存。由于进程虚拟地址空间独立。而TLB的访问速度比MMU快的多。因此,通过减少进程的上下文切换,减少TLB的刷新次数,可以提高其缓存使用率,进而提高CPU的内存访问性能。
3、MMU不是以字节为单位来管理内存,而是规定了内存映射的最小单位,。通常4kb大小。

3、多级页表和大页

页的大小为4KB,导致整个页表会非常大。如,仅32位系统就需要100多万个页表项(4GB/4KB),才能实现整改地址空间的映射。为了解决页表项过多,Linux提供了两种机制,多级页表和大页(HugePage)。
1、多级表就是把内存分成区块来处理,将原来的映射关系改为区块索引和区块内的偏移。由于虚拟内存空间通常只用了很少一部分,那么,多级页表就只保存这些使用中的区块,大大减少页表的项数。
Linux用的四级页表来管理内存页。虚拟地址被分为5个部分,前4个表用于选择页,最后一个索引表示 页内偏移。
image.png
2、大页,就是比普通页更大的内存块,常见有 2MB 和 1GB,通常用在使用大量内存的进程上。

2、虚拟内存空间分布

以32位系统为例
image.png
堆和文件映射段的内存是动态分配的。如C中的 malloc()mmap() ,可以在堆和文件映射段动态分配内存。

3、内存分配与回收

1、分配

malloc() 是 C 标准库提供的内存分配函数,对应到系统调用上,有两种实现方式,即 brk() 和 mmap()。
1、小块内存(小于128k),C标准库使用 brk() 来分配也就是通过移动栈顶的位置来分配内存。这些内存释放后不会立刻归系统,而是被缓存,用来重复利用;
可以减少缺页异常的发生,提高访问效率。不过,由于内存没有归还系统,在内存工作繁忙的时候,频繁的内存分配和释放会造成内存碎片
2、大块内存(大于128K),直接使用内存映射 mmap() 来分配,也就是在文件映射段找一块空闲内存分配出去;
会直接归还系统,每次mmap都会发生缺页异常。在内存工作繁忙的时候,频繁的内存分配会导致大量的缺页异常,使内核的管理负担增大。

这两种调用发生后,并没有真正分配内存。都只有在首次访问时才分配,也就是通过缺页异常进入内核中,再由内核来分配。

  • 在用户空间,malloc 通过 brk() 分配的内存,在释放时,并不立即归还系统,而是缓存起来重复利用
  • 在内核空间,linux通过 slab 分配器来管理小内存。slab主要分配并释放内核中的小对象

    2、回收

  • 缓存回收,如LRU算法,回收使用最少的内存页面

  • 回收不常访问的内存,把不常使用的内存通过交换分区(Swap)直接写到磁盘中

Swap就是把一块磁盘空间当成内存来用。它可以把进程暂时不用的数据存储到磁盘中(换出),当进程访问这些内存时,再从磁盘读取这些数据到内存中(换入)。会将系统的可用内存变大,不过,通常在内存不足的情况,才发生 Swap交换。由于磁盘读写速度远比内存差,Swap会导致严重的内存性能问题。

  • 杀死进程,内存紧张时,系统通过OOM(Out of Memory),直接杀掉占用大量内存的进程

是内核的一种保护机制,监控进程的内存使用情况,并使用 oom_score 为每个进程的内存使用情况评分。
(一个进程消耗的内存越大,oom_score越大;一个进程运行占用的CPU越多,oom_score越小)

1、LRU回收算法

回收缓存和Swap回收,都基于LRU算法。
维护 active 和 inactive 两个双向链表,其中

  • active:记录活跃的内存页,即文件页,对应 缓存回收
  • inactive:记录非活跃的内存页,即匿名页,对应 Swap 回收

越接近链表尾部,表示内存页越不常访问。

2、OOM

当系统发现内存不足以分配新的内存请求时,就会尝试直接内存回收。
但如果内存还是不足,就需要OOM,可以在 dmesg 中看到 Out of memory 的信息,从而知道是哪些进程被 OOM 杀死了。
如果不希望应用程序被 OOM 杀死,可以调整进程的 oom_score_adj,减小 OOM 分值。

4、查看内存使用情况

1、free

image.png
默认字节单位。

  • total:总内存大小
  • used:已使用内存大小,包含共享内存
  • free:未使用内存大小
  • shared:共享内存大小
  • buff/cache:缓存和缓冲区大小
  • available:新进程可用内存大小

可用内存 available 不仅包含未使用内存,还包括可回收的缓存,所以一般比未使用内存大。

2、top

image.png

  • VIRT:进程虚拟内存大小,只要是进程申请过的内存,即使没有真正分配物理内存,也会计算在内
  • RES:常驻内存大小,也就是进程实际使用的物理内存大小,不包括 Swap 和共享内存
  • SHR:共享内存大小,如与其他进程共同使用的共享内存、加载的动态链接库、程序的代码等
  • %MEM:进程使用物理内存占系统总内存百分比