虚拟内存与物理内存
计算机系统将内存组织成固定大小的块(chunks),称为页(page)。页面的大小取决于处理器架构;对于x86_64,标准页面大小是4KiB。系统上的物理RAM被分为页帧(page frame);一页帧保存一页数据。
当进程请求内存时,内核将页帧的物理地址映射到进程地址空间中的虚拟地址
进程虚拟地址空间的大小不依赖于安装的物理RAM,而是依赖于处理器架构。在64位x86-64系统上,虚拟地址空间大小为2的64次幂字节(16 EiB)。
单个进程通常不会使用它的整个地址空间。它的大部分是未分配的,也没有映射到任何实际的物理内存。
虚拟内存空间很大,但只有其中一部分映射到了物理内存中
ps和top等工具区分了两种统计数据:
VIRT/VSZ,进程请求的虚拟内存总量(不重要)
RES/RSS,进程映射到物理内存的量(重要)
使用cgroup限制内存使用
1 | [Service] |
页面错误
当进程访问一个系统还没有映射到表中的物理页帧的页面时,内核会生成一个页错误事件(page fault),这是常见的,当进程在分配后第一次访问页面时,发生该事件。在这种情况下,内核会生成一个**小页面错误(minor page fault)**,并从RAM中分配一个新的物理页面帧。
比如内存不足,如果缺少物理页帧,并且系统从磁盘检索该内存页,就会产生重大页错误,此时内核需要从磁盘加载页面,如果成百上千就说明非常多了
1 | ps -o pid,minflt,majflt,comm -C nginx |
页表与TLB
页表分页
页表采用分层结构(多级页表)的核心目的是为了解决 虚拟地址空间规模庞大导致单级页表内存占用过高 的问题,同时兼顾 灵活的内存管理 和 硬件实现效率
但是页表分页的开销太大了,需要一张表一张表查询
TLB
每次地址转换需查询页表(Page Table),但频繁访问内存中的页表效率极低。TLB作为页表的 硬件缓存,存储最近使用的页表项(虚拟页号→物理页框号),避免重复访问内存中的页表。
TLB是Translation Lookaside Buffer的缩写,中文通常翻译为“转译后备缓冲器”或“页表缓存,类似于CPU缓存,但专门用于存储页表项
如图:
先查找TLB,如果没有就查页表,要是还是找不到就从磁盘中加载到内存
整个内存访问过程
CPU通过虚拟地址访问内存时,需借助页表将其转换为物理地址。由于虚拟地址空间极大,单级页表会导致内存浪费,因此采用多级页表按需分配内存。若TLB未命中,需遍历多级页表(产生额外延迟),但仅当物理页不在内存时才会触发缺页异常(Page Fault)。TLB作为页表项的高速缓存,直接存储热点虚拟页到物理页的映射,极大加速地址转换过程
大页内存
TLB的条目数量是固定的,对于访问大量内存的进程比如数据库,可能会导致大量TLB丢失,从而影响性能
linux支持通过内核调整,将内存页调整为4kib,2Mib,1Gib
针对大页内存,每个TLB就可以寻找到2Mib的页面,这样TLB命中率就会更高
上下文切换时, TLB会被重新填充。
可以通过内核参数vm.nr_hugepages来设置大内存页的总数
1 | sysctl -w vm.nr_hugepages=40 |
通过将内存分配为大页面,只有支持大页面的应用程序才能使用这些内存。内核从一般可用的系统内存中移除巨大的页面内存,大多数进程都不能使用它。
1 | 针对NUMA系统的大页分配 |
透明大页THP
从红帽6.2开始,内核默认开启透明大页技术
透明大页(Transparent Huge Pages, THP) 是 Linux 内核提供的一种自动化内存管理技术,旨在通过动态调整2Mib和1Gib内存页的数量,来提升性能
1 | # 查看当前状态 |
内存分页、回收与swap
页缓存
内核使用大部分未分配的内存,作为缓存来存储从磁盘读取或写入的数据
匿名页
用以进程分配和存储工作数据的页,可以通过cat /proc/meminfo | grep -i anon查看
这两个都算在buff/cache中
1 | MemAvailable ≈ MemFree + Cached + Buffers + SReclaimable - 保留内存 |
匿名页的回收
当系统处于内存使用压力时,内核可以选择回收匿名页或从页缓存中回收页。不受文件支持的匿名页被移到swap区。
随着空闲内存的减少,内核可以换出进程没有积极使用的内存页。然后将这些回收的页面用于其他请求。
页缓存的回收
当内核需要释放内存页时,它有两种选择。它可以将进程匿名页换出到交换区域,也可以从页缓存中删除页。通过vm.swappiness参数(0~100),可以影响内核的选择
设置vm.swappiness参数设置为100,系统几乎总是倾向于将匿名页面分页到交换区域,而不是从页面缓存中回收页面。
设置vm.swappiness参数设置为1,则迫使系统尽可能少地进行交换。这种调优可能会使系统响应更迅速,但以牺牲文件系统性能为代价。像数据库在内存中保存数据,就可以从低vm.swappiness中获益
可以使用cgroup通过memory.swappiness,也可以单独进程进行vm.swappiness的设置
脏页面清理与调优
一个物理页可以处于不同状态:
Free-可以立即分配
Active-正在被使用,不可被释放
Inactive clean-没有被积极使用,且内容与磁盘一致
Inactive dirty-没有被积极使用,且内容与磁盘不一致,脏页
脏页
当进程进行写操作时,操作的数据正好在缓存中,就会产生脏页,会以一定周期进行写入磁盘
1 | cat /proc/meminfo | grep -i dirty |
OOM与优先级(不良评分
一旦系统达到了内存不足的情况,就没有太多合理的恢复选项。关闭进程以释放内存或放弃并关闭系统都是可能的选择
每个进程都有一个不良评分,可以在/proc/PID/oom_score文件中查看,使用它来确定哪个进程应该被OOM杀死。分数越高,OOM杀手就越有可能杀死这个过程
如果进程在cgroup中限制了内存,也会触发OOM
如果NUMA系统中节点没有多余内存,也会触发OOM
1 | 不良评分的影响因素 |
内存过量分配
进程请求内存的时候,只保留虚拟内存记录,并不实际分配内存,所以就会可能过度使用内存
虚机与容器也是主机上的一组进程
如果空闲RAM快用完,系统会按照通常的方式回收页面:从页面缓存中回收,或者将不活动的匿名页面换出到交换区。当机器处于内存过度分配的情况下,现在必须交付比可能的更多的内存时,内核调用OOM杀死器杀死一些进程。
1 | vm.overcommit_memory可调项控制系统的内存过度分配策略 |