目录
虚拟内存
内存分配过程
直接内存回收和后台内存回收
回收内存的触发标准
那些内存被回收呢?
内存回收后,内存还是不够怎么办呢?
虚拟内存
介绍操作系统内存如何使用时,不可以避免的先认识到虚拟内存
首先我们通过虚拟内存的作用,来认识一下:
1.虚拟内存可以使得进程对运行内存超过物理内存大小,因为程序运行符合局部性原理,CPU 访问内存会有很明显的重复访问的倾向性,对于那些没有被经常使用到的内存,我们可以把它换出到物理内存之外,比如硬盘上的 swap 区域。
我们为一个进程分配内存时,分配的是一个虚拟内存,如果我们分配的是物理内存那么,当这个进程大部分东西没有被使用时,就造成了内存的浪费,所以当我们真正使用时才分配真实的物理内存
2.由于每个进程都有自己的页表,所以每个进程的虚拟内存空间就是相互独立的。进程也没有办法访问其他进程的页表,所以这些页表是私有的,这就解决了多进程之间地址冲突的问题。
物理地址是虚拟地址通过页表映射而来的,每个进程都有自己的页表,
那么每个进程访问真实的物理地址互不干扰,因为中间隔着虚拟地址
3.页表里的页表项中除了物理地址之外,还有一些标记属性的比特,比如控制一个页的读写权限,标记该页是否存在等。在内存访问方面,操作系统提供了更好的安全性。
避免了用户态直接访问内核态的物理地址
内存分配过程
直接内存回收和后台内存回收
当使用malloc动态的分配内存时,我们知道哪分配的是虚拟内存,而不是真实的物理内存
当应用程序读写了这块虚拟内存,CPU 就会去访问这个虚拟内存,
这时会发现这个虚拟内存没有映射到物理内存, CPU 就会产生缺页中断,
进程会从用户态切换到内核态,保存现场(保存PC指针)和上下文,
并将缺页中断交给 内核的缺页中断函数 处理。缺页中断处理函数会看是否有空闲的物理内存,
如果有,就直接分配物理内存,并建立虚拟内存与物理内存之间的映射关系。
如果没有空闲的物理内存,那么内核就会开始进行回收内存的工作,
回收的方式主要是两种:直接内存回收和后台内存回收。
后台内存回收:
后台内存回收:在物理内存紧张的时候,
会唤醒 kswapd 内核线程来回收内存,这个回收内存的过程异步的,不会阻塞进程的
直接内存回收: 如果后台异步回收跟不上进程内存申请的速度,就会开始直接回收,
这个回收内存的过程是同步的,会阻塞进程的执行。(阻塞申请内存的那个进程)
影响:造成很长时间的延迟,以及系统的 CPU 利用率会升高,最终引起系统负荷飙高。
回收内存的触发标准
下面用图解释
当然你可以调整这个阈值的大小,从而尽早的运行后台内存回收,从而避免内存紧张
那些内存被回收呢?
主要有两类内存可以被回收,而且它们的回收方式也不同。
文件页:内核缓存的磁盘数据和内核缓存的文件数据都叫作文件页。
大部分文件页,都可以直接释放内存,以后有需要时,再从磁盘重新读取就可以了。
而那些被应用程序修改过,并且暂时还没写入磁盘的数据(也就是脏页),
就得先写入磁盘,然后才能进行内存释放。
所以,回收干净页的方式是直接释放内存,回收脏页的方式是先写回磁盘后再释放内存。
匿名页:这部分内存没有实际载体,不像文件缓存有硬盘文件这样一个载体,比如堆、栈数据等。这部分内存很可能还要再次被访问,所以不能直接释放内存,
它们回收的方式是通过 Linux 的 Swap 机制,Swap 会把不常访问的内存先写到磁盘中,
然后释放这些内存给其他更需要的进程使用。再次访问这些内存时重新从磁盘读入内存就可以了。
文件页 和 内存页 又依靠什么机制来淘汰呢?
MySQL如何改进LRU算法-CSDN博客
哪当然是Linux的LRU,类似我这篇博客里MySQL的LRU算法,
Linux的LRU 分为两个链表 一个活跃链表, 一个非活跃链表
所以当我们 回收时 就从非活跃链表 淘汰页(把这些页从内存中淘汰,下次调用从磁盘拿)
内存回收后,内存还是不够怎么办呢?
触发 OOM (Out of Memory)机制
OOM Killer 机制会根据算法选择一个占用物理内存较高的进程,然后将其杀死,
以便释放内存资源,如果物理内存依然不足,
OOM Killer 会继续杀死根据算法选择占用物理内存较高的进程,直到释放足够的内存位置。
算法:
1. 进程使用的物理内存量:
// points 代表打分的结果 // process_pages 代表进程已经使用的物理内存页面数 // oom_score_adj 代表 OOM 校准值 // totalpages 代表系统总的可用页面数 points = process_pages + oom_score_adj*totalpages/1000
2.特权进程的评分:特权进程通常较为重要,因此将其得分设置为1/4。例如守护进程 (1号进程)