内存分段 🆚 内存分页
参考博客
内存碎片🧩
内部内存碎片
外部内存碎片
解决的办法就是内存交换:即把某个程序占用的内存先写到硬盘中,接着释放其占用的内存空间;之后再把信息读回到内存中。不过再读回时占用的内存和之前占用的内存不一样,而是紧邻之前分配过的内存,以期减少外部内存碎片。
内存交换空间,从硬盘划分出来,用于内存与硬盘的交换,对应Linux中的Swap分区
内存分段
按需分配内存,因此不会有内部碎片;但是每个程序需要使用的每个段大小不一样,所以多个段未必能恰好使用所有的内存空间;因此会产生很多不连续的小内存,产生外部碎片问题。
此外,内存分段使得发生内存交换时,需要交换一块很大的区域,因此导致内存交换的效率很低
内存分页
分段的好处是能产生连续的物理内存空间,但是会出现「外部内存碎片和内存交换时数据太大」的问题。
「分页」:把物理内存和虚拟内存切成一块块固定大小的尺寸,页
内存分页机制分配内存的最小单位是页,因此不会出现外部内存碎片。但是即使程序使用内存不足一页,也要为其分配一个页的内存,因此内部会有部分空间浪费,因此内存分页机制会出现内部碎片。
如果内存不够,OS会把其它进程中「最近未被使用」的内存页上的信息写到磁盘中,并释放对应的内存空间,这叫做换出;当这部分信息需要的时候,再把它加载到内存中,这被称为换入。所以一次性换入/出的也只有少数的几个页,因此内存交换的效率较高。
进一步的,程序可以使用延迟加载的机制;在需要的时候再把数据加载到物理内存中。
使用多级页表进一步降低存储页表需要的内存空间。对于二级页表来说,占用4KB的1024个一级页表项就可以覆盖4G的物理内存空间;且一个程序使用到的物理内存远小于4G,因此有很多的二级不需要建立 => 空间局部性原理
段页式内存管理
X86的内存分页机制
分页运行在分段基础上:先由分段管理机制将程序的「逻辑地址」转换为「线性地址」(虚拟地址),再由页式管理单元将「线性地址」转换为「物理地址」。
为了屏蔽x86的这一硬件要求,运行在x86上的Linux中的每个段都是从0开始的整个4GB地址空间,因此OS代码、应用程序代码看到的都是线性地址
程序虚拟内存布局
每个程序都有自己的页表
用户空间布局
用户空间最上方:栈 -> 「待分配区域」| 文件映射区 | 「待分配区域」<- 堆 <->「 BSS」、「数据段」、「代码段」、「保留区」。
- malloc : 从堆区分配内存
- mmap : 从文件映射区分配内存
- 文件映射区:还会存放动态库中的代码段、数据段、BSS段