首页 C++内存管理- 12 ~ 13
文章
取消

C++内存管理- 12 ~ 13

C++内存管理 - 12~13

此笔记需要等待更新和确认。

Per-class allocator - 自己的内存池(1)

微信图片_20220531051103

new char[chunk]是因为char是1个字节。举例,char[100]就是100字节。所以char[chunk]就是分配了对应大小的内存

实现过程

  1. 预申请一个内存区chunk,将内存中按照对象大小划分成多个内存块block。
  2. 维持一个空闲内存块链表,通过指针相连,标记头指针为第一个空闲块。
  3. 每次新申请一个对象的空间,则将该内存块从空闲链表中去除,更新空闲链表头指针。
  4. 每次释放一个对象的空间,则重新将该内存块加到空闲链表头。
  5. 如果一个内存区占满了 if(!freestore),则新开辟一个内存区,维持一个内存区的链表,同指针相连,头指针指向最新的内存区,新的内存块从该区内重新划分和申请。

QQ截图20220531054937

更正,上一张图的第三部分的第一行。设置释放节点的下一个节点为当前链表头 更正为 设置释放节点的下一个节点为原本的链表头。也就是让释放的内存区块和原本链表头连接上。然后让释放的内存区块成为新的链表头。

Per-class allocator - 自己的内存池(2)- 改进next指针

通过上面的实现,我们发现每一个区块都要维护一个自己的next指针还是有一定开销的。我们能不能把这个next指针优化掉呢?

我们要引入一个概念,union联合体

union简而言之就是在union结构体中的变量,同一时刻只可以有一个变量有值。而且union结构体的大小是结构体中最大的那个元素的大小。也就是说,union结构体中变量是覆盖的,也就是共享一块内存。

底层内存分配几乎都是用使用union联合体来节约空间。

为什么我们会想到使用这种方法呢?我们发现next指针只有三种情况会被用到:

  1. 第一次分配对象,建立整个freeStore的链表的时候,初始化各个实例的next指针

  2. 每次给申请者一个实例时,要修改freeStore = freeStore->next

  3. 每次归还一个实例时,链表插入操作要用到next.

简而言之,当区块被分配出去实际储存数据的时候,是不需要next指针的。只有区块处于空闲状态时或者将要处于空闲状态时才需要。也就是说区块的next指针和区块实际分配的内容是互斥的,两者不会同时存在。

这样就是说我们可以在一块内存中,如果被分配出去了,那就将这块内存全部用来储存对象,如果没有被分配出去或者是归还了就用来储存next指针。

我们说过了union的大小是结构体中最大的部分的大小。举个例子,如果有这样的union结构体

1
2
3
4
union test{
	int a;
	int * next;
}

这个union结构体会占用8字节(64位操作系统)。当分配出去的时候,8个字节用来储存对象。当空闲的时候,前四个字节用来储存指针。

这样的指针我们叫做嵌入式指针

QQ截图20220531114027

union 和 类型截断 和 大端序 小端序

intel的机器是小端序。

大端序就是假如输入是0x01020304,则保存也是0x01020304

小端序就是反过来。变成0x04030201。

所以类型截断的时候,比如

1
2
3
4
5
6
7
8
9
union test{
    long long a;
    short b;
}
int main(){
    test t;
    t.a = INT32_MAX;
    cout << b << endl; //输出-1
}

因为long long是64字节,又是有符号数。所以是1个0和63个1。

由于union的大小是结构中最大的数据的大小。所以现在union内存中是0111….1 一共64位。

由于是小端序,也就是起始地址是我们的低位。所以16进制0xfdffff….f在储存中会变成0xffff….fd。所以会从低地址开始截断。也就是会截取出16个1。自然输出就是-1。

本文由作者按照 CC BY 4.0 进行授权