- 堆和栈是在日常开发中常常会用到的两种内存空间,其中栈的作用主要是用于存储程序运行时的各种变量,并且,每一段程序都可以有自己的栈用于存储当前的局部变量等信息,栈的内存受到编译器管理,栈内存的释放与使用都由系统调用。而堆则是可以人为的对其进行使用和释放。
- 栈的空间相对于堆较小,由操作系统指定大小并自动分配和管理
而对于堆来说,内存是由程序员手动释放或由GC自动回收,因此我们如何对堆进行内存管理呢?一种简单的方式就是使用链表,假设在内存中,我们申请了1KB的内存也就是1024字节的内存用作堆的内存,我们首先需要定义一个结构体
//全局的头结点
typedef struct HEAD{
int size = 0;
struct* HEAD next_free ;
} ;
这个结构体中定义了当前内存的长度以及下一个可用地址的位置,这个是全局结构体,实际上不起作用,作用是为了指向第一个可用空闲内存的头部,可以发现定义的大小是0,并且用结构体指针next_free指向下一个结构体
-
此时我们申请100byte的空间,这时在这100byte空间前就会创建一个新的结构体用于描述这100byte,同时也方便后续释放该空间,用于描述这100byte空间的头部可以表示为
//该结点用于表示申请到的空间
typedef struct POINT{
int size = 100;
struct* POINT next_free = null;
};point0
该头部表示指向的内存为100byte,并且指向下一个结点,但是由于我们目前只申请了一块空间,目前结点只有全局结点和这个100byte的结点,因此该结点没有下一个结点,next_free=null。此时全局结点中的next_free就可以指向这个100byte的结点。
//此时头结点指向申请到的第一个空间的结点
typedef struct HEAD{
int size = 0;
struct* HEAD next_free = point0;
} ;
如果此时我们想要访问这个结点只需要使用全局变量生成一个实例即可,因为此时全局变量中的next_free是指向了这个100byte的结点的,当然实际使用时是已经实例化了全局结点的,不然无法修改全局结点内的指针指向。
typedef struct HEAD{
int size = 0;
struct* HEAD next_free;
} ;
typedef struct POINT{
int size = 100;
struct* POINT next_free;
};point0
int main(){
HEAD *head;
head = &point0;
point0.next_free = null;
}
- 但是因为结点也需要内存的原因,因此实际使用的内存是108byte(32位ARM情况),此时我们又需要一块50byte大小的内存和一块200byte大小的内存,如法炮制我们可以创建新的结点,并且将第一个结点point0的next_free指向下一个结点point1的地址,point1的next_free指向下一个结点point2的地址。代码就不多赘述。
目前我们已经有三个结点了,假如此时我们删除中间的那个结点,也就是第二个结点,那么在宏观上来看就会在第一段申请的内存和第三段申请的内存中空出一块,那么此时解决的方法也很简单,只需要将第三个结点的指向指为第二个结点即可。然后第二个结点的next_free=null,下一次使用空闲位置时会先判断被删除的部分是否够用,不够再去开辟新的内存,因此也会导致一个问题就是内存的碎片化,会使得很多很小的内存没有被使用到。