经常看别人博客说到“内存碎片”这个概念,而且很多编程技巧书也经常提到频繁地调用内存分配函数会导致越来越多的内存碎片产生,降低内存的利用率。那么这个“内存碎片”究竟是怎么产生的呢?答案可能要从“操作系统”中查找了。
在翻阅了一些《操作系统》的书之后,对于这个概念有了更清晰的理解,下面进行总结:
当我们运行一个程序时,处理器要生成一个进程,并把程序载入内存。那么内存是如何分区的呢?是分成固定大小的一块一块么?的确存在这种设计方案,那么我们看看针对这种情况,碎片会出现在哪里?假设我们的一个分区是8M,我们的进程大小是3M,那么当我们的进程装入分区后,分区里面剩余的5M我们并没有用到,而且由于这个分区其他进程也不能同时使用,所以这5M就被浪费了,这种浪费其实就是“碎片”,由于这种碎片出现在分区内,一般我们称它为“内部碎片”。那么我们是否可以使用大小不同的分区呢?比如分区大小从小到大排列 2M、4M、6M、8M、10M。这种方案会缓解这个问题,但并不能完全解决。
那么是否有其他方案可供我们选择呢?我们是不是可以尝试动态分区,我们的进程有多大,分区大小就为多大。这种方案当然也存在,假设我们先装入一个进程1:20M,然后再装入进程2:14M,再装入一个进程3:18M。接下来进程2运行结束被换出,接着运行一个进程4:8M。现在加入我们要继续装入一个大小为9M的进程的话,会发现已经没有空闲间隙给我们。可实际上内存还留有10M的空间,只是它们并没有连续。所以我们现在遇到了空间还够却无法装入进程的窘境。那些空隙我们就可以称为“外部碎片”,也就是说采用动态分区的方案会产生“外部碎片”。
缺点:当我们多个程序共享内存时,无法确定哪块区域一定是留给你用的。
由于内存压缩十分耗时,,因而操作系统需要巧妙地把进程分配到内存中,以便盖住那些“空隙”。一般来说有三种放置算法:
1.最佳适配:找到最接近进程大小的空隙
2.首次适配:从头开始找第一个可以放下进程的空隙
3.下次适配:从上次找到的空隙往后找可以放下进程的空隙
首次适配一般的结果是最好最优的,下次适配会使得存储空间末尾的最大空闲存储块很快分裂为小碎块,因此使用下次匹配可能会使得更多次的压缩。最佳适配尽管每次都能找到最匹配空间的空隙,但性能太差,类似顺序遍历查找最大值最小值的算法是O(n)的效率一样,平均要扫描整个内存块。
大小不等的固定分区和大小可变的分区技术在内存上的使用都是低效的,前者会产生内部碎片,后者会产生外部碎片。如果我们采用分页技术,将进程分为固定大小的块,将内存也分为固定大小的块。那么进程的一块可以称为“页”,内存中的一块可以称为可以称为“页框”。一般来说每一页是4K的样子,使用分页技术我们每个进程浪费的空间最多就是进程最后一页的一小部分。很小的内部碎片,但是没有外部碎片。