我们发现,现在可用内存空间为3,但是,这3个空闲空间,并不是连续的。
所以,如果程序现在申请长度为3的内存空间,同样会申请不了,会出现内存不够。我们把这种情况,称之为内存碎片。
那内存碎片怎么解决呢?于是就有了分页机制,接下来我们详细讲一下分页:
首先,把物理内存,按照某种尺寸,进行平均分割。比如我现在以2个内存单位,来分割内存,也就是每两个连续的内存空间,组成一个内存页:
0 0 空闲
1
2 1 空闲
3
4 2 空闲
5
6 3 空闲
7
8 4 空闲
9
接着,系统同样需要维护一个内存信息表: ID 使用的内存页ID
现在,程序申请长度为3的内存空间,不过由于现在申请的最小单位为页面,而一个页面的长度为2,因此现在需要申请2个页面,也就是4个内存空间。你看,这就浪费了1个内存空间。
地址 页ID 状态0 0
1
2 1
3
4 2 空闲
5
6 3 空闲
7
8 4 空闲
9
ID 使用的内存页ID
0 0,1
接着,程序再申请长度为1,长度为2的空间:
地址 页ID 状态0 0
1
2 1
3
4 2
5
6 3
7
8 4 空闲
9
ID 使用的内存页ID
0 0,1
1 2
2 3
释放掉ID=1,内存页ID为2的那条内存空间信息:
地址 页ID 状态0 0
1
2 1
3
4 2 空闲
5
6 3
7
8 4 空闲
9
ID 使用的内存页ID
0 0,1
2 3
现在,就出现了之前的情况:目前一共有4个内存空间,但是不连续。
不过,因为现在是分页管理机制,因此,现在仍然可以继续申请长度为4的内存空间。
没有碎片,能够尽量地全部用完空间。但仔细想想,这种优势背后,也是需要付出大量代价的。
分页的方式下,程序需要记录内存页ID,每次使用时,需要从内存页ID翻译成实际内存地址,多了一次转换。
而且这种模式,会浪费一些内存,比如上面申请3个内存空间,实际分配了2个页面共4个内存空间,浪费了1个内存空间。
还有一个要注意的地方,这个时候"段基址+段内偏移地址"经过段部件处理后得到的线性地址就不再是物理地址了,而是虚拟地址了。
从下图我们能够清楚的看出来我们最后的线性地址表示的是页表的地址,而不是物理地址了。