【扩展知识】数据结构之动态内存管理机制!

在使用早期的计算机上编写程序时,有关数据存储在什么位置等这样的问题都是须要程序员本身来给数据分配内存。程序员

而如今的高级语言,大大的减小了程序员的工做,不须要直接和存储空间打交道,程序在编译时由编译程序去合理地分配空间。编程

本文重点解决的问题是:学习

    ✪ 对于用户向系统提出的申请空间的请求,系统如何分配内存?spa

    ✪ 当用户不在使用以前申请的内存空间后,系统又如何回收?3d

这里的用户,不是普通意义上的用户,多是一个普通的变量,一个应用程序,一个命令等等。只要是向系统发出内存申请的,均可以称之为用户。指针

 

占用块和空闲块

对于计算机中的内存来讲,称已经分配给用户的的内存区统称为“占用块”;还未分配出去的内存区统称为“空闲块”或者“可利用空间块”。视频

 

系统的内存管理

对于初始状态下的内存来讲,整个空间都是一个空闲块(在编译程序中称为“堆”)。可是随着不一样的用户不断地提出存储请求,系统依次分配。blog

整个内存区就会分割成两个大部分:低地址区域会产生不少占用块;高地址区域仍是空闲块。以下图所示:排序


 

可是当某些用户运行结束,所占用的内存区域就变成了空闲块,以下图所示:ip


 

此时,就造成了占用块和空闲块犬牙交错的状态。当后续用户请求分配内存时,系统有两种分配方式:

    一、系统继续利用高地址区域的连续空闲块分配给用户,不去理会以前分配给用户的内存区域的状态。直到分配没法进行,也就是高地址的空闲块不能知足用户的需求时,系统才会去回收以前的空闲块,从新组织继续分配;

    二、当用户运行一结束,系统立刻将其所占空间进行回收。当有新的用户请求分配内存时,系统遍历全部的空闲块,从中找出一个合适的空闲块分配给用户。

合适的空闲块指的是可以知足用户要求的空闲块,具体的查找方式有多种,后续会介绍。

 

可利用空间表

当采用第 2 种方式时,系统须要创建一张记录全部空闲块信息的表。表的形式有两种:目录表和链表。各自的结构以下图所示:


 

目录表:表中每一行表明一个空闲块,由三部分组成:

    ✪ 初始地址:记录每一个空闲块的起始地址。

    ✪ 空闲块大小:记录每一个空闲块的内存大小。

    ✪ 使用状况:记录每一个空闲块是否存储被占用的状态。

链表:表中每一个结点表明一个空闲块,每一个结点中须要记录空闲块的使用状况、大小和链接下一个空闲块的指针域。

因为链表中有指针的存在,因此结点中不须要记录各内存块的起始地址。

————————

系统在不一样的环境中运行,根据用户申请空间的不一样,存储空闲块的可利用空间表有如下不一样的结构:

    一、若是每次用户请求的存储空间大小相同,对于此类系统中的内存来讲,在用户运行初期就将整个内存存储块按照所需大小进行分割,而后经过链表连接。当用户申请空间时,从链表中摘除一个结点归其使用;用完后再连接到可利用空间表上。

    二、每次若是用户申请的都是若干种大小规格的存储空间,针对这种状况能够创建若干个可利用空间表,每个链表中的结点大小相同。当用户申请某一规格大小的存储空间时,就从对应的链表中摘除一个结点供其使用;用完后连接到相同规格大小的链表中。

    三、用户申请的内存的大小不固定,因此形成系统分配的内存块的大小也不肯定,回收时,连接到可利用空间表中每一个结点的大小也各不同。

第 2 种状况下容易面临的问题是:若是同用户申请空间大小相同的链表中没有结点时,就须要找结点更大的链表,从中取出一个结点,一部分给用户使用,剩余部分插入到相应大小的链表中;回收时,将释放的空闲块插入到大小相同的链表中去。若是没有比用户申请的内存空间相等甚至更大的结点时,就须要系统从新组织一些小的连续空间,而后给用户使用。

 

分配存储空间的方式

一般状况下系统中的可利用空间表是第 3 种状况。如图 3(C) 所示。因为链表中各结点的大小不一,在用户申请内存空间时,就须要从可利用空间表中找出一个合适的结点,有三种查找的方法:

    ✪ 首次拟合法:在可利用空间表中从头开始依次遍历,将找到的第一个内存不小于用户申请空间的结点分配给用户,剩余空间仍留在链表中;回收时只要将释放的空闲块插入在链表的表头便可。

    ✪ 最佳拟合法:和首次拟合法不一样,最佳拟合法是选择一块内存空间不小于用户申请空间,可是却最接近的一个结点分配给用户。为了实现这个方法,首先要将链表中的各个结点按照存储空间的大小进行从小到大排序,由此,在遍历的过程当中只须要找到第一块大于用户申请空间的结点便可进行分配;用户运行完成后,须要将空闲块根据其自身的大小插入到链表的相应位置。

    ✪ 最差拟合法:和最佳拟合法正好相反,该方法是在不小于用户申请空间的全部结点中,筛选出存储空间最大的结点,从该结点的内存空间中提取出相应的空间给用户使用。为了实现这一方法,能够在开始前先将可利用空间表中的结点按照存储空间大小从大到小进行排序,第一个结点天然就是最大的结点。回收空间时,一样将释放的空闲块插入到相应的位置上。

 

以上三种方法各有所长:

    ✪ 最佳拟合法因为每次分配相差不大的结点给用户使用,因此会生成不少存储空间特别小的结点,以致于根本没法使用,使用过程当中,链表中的结点存储大小发生两极分化,大的很大,小的很小。该方法适用于申请内存大小范围较广的系统

    ✪ 最差拟合法,因为每次都是从存储空间最大的结点中分配给用户空间,因此链表中的结点大小不会起伏太大。依次适用于申请分配内存空间较窄的系统。

    ✪ 首次拟合法每次都是随机分配。在不清楚用户申请空间大小的状况下,使用该方法分配空间。

同时,三种方法中,最佳拟合法相比于其它两种方式,不管是分配过程仍是回收过程,都须要遍历链表,全部最费时间。

 

空间分配与回收过程产生的问题

不管使用以上三种分配方式中的哪种,最终内存空间都会成为一个一个特别小的内存空间,对于用户申请的空间的需求,单独拿出任何一个结点都不可以知足。

可是并非说整个内存空间就不够用户使用。在这种状况下,就须要系统在回收的过程考虑将地址相邻的空闲块合并。


 

最后,无论你是转行也好,初学也罢,进阶也可,若是你想学编程~

【值得关注】个人 C/C++编程学习交流俱乐部【点击进入】

问题答疑,学习交流,技术探讨,还有超多编程资源大全,零基础的视频也超棒~

相关文章
相关标签/搜索