C++内存管理 - 12~13
此笔记需要等待更新和确认。
Per-class allocator - 自己的内存池(1)
new char[chunk]是因为char是1个字节。举例,char[100]就是100字节。所以char[chunk]就是分配了对应大小的内存
实现过程
- 预申请一个内存区chunk,将内存中按照对象大小划分成多个内存块block。
- 维持一个空闲内存块链表,通过指针相连,标记头指针为第一个空闲块。
- 每次新申请一个对象的空间,则将该内存块从空闲链表中去除,更新空闲链表头指针。
- 每次释放一个对象的空间,则重新将该内存块加到空闲链表头。
- 如果一个内存区占满了
if(!freestore)
,则新开辟一个内存区,维持一个内存区的链表,同指针相连,头指针指向最新的内存区,新的内存块从该区内重新划分和申请。
更正,上一张图的第三部分的第一行。设置释放节点的下一个节点为当前链表头 更正为 设置释放节点的下一个节点为原本的链表头。也就是让释放的内存区块和原本链表头连接上。然后让释放的内存区块成为新的链表头。
Per-class allocator - 自己的内存池(2)- 改进next指针
通过上面的实现,我们发现每一个区块都要维护一个自己的next指针还是有一定开销的。我们能不能把这个next指针优化掉呢?
我们要引入一个概念,union联合体
union简而言之就是在union结构体中的变量,同一时刻只可以有一个变量有值。而且union结构体的大小是结构体中最大的那个元素的大小。也就是说,union结构体中变量是覆盖的,也就是共享一块内存。
底层内存分配几乎都是用使用union联合体来节约空间。
为什么我们会想到使用这种方法呢?我们发现next指针只有三种情况会被用到:
第一次分配对象,建立整个
freeStore
的链表的时候,初始化各个实例的next指针每次给申请者一个实例时,要修改
freeStore = freeStore->next
每次归还一个实例时,链表插入操作要用到
next
.
简而言之,当区块被分配出去实际储存数据的时候,是不需要next指针的。只有区块处于空闲状态时或者将要处于空闲状态时才需要。也就是说区块的next指针和区块实际分配的内容是互斥的,两者不会同时存在。
这样就是说我们可以在一块内存中,如果被分配出去了,那就将这块内存全部用来储存对象,如果没有被分配出去或者是归还了就用来储存next指针。
我们说过了union的大小是结构体中最大的部分的大小。举个例子,如果有这样的union结构体
1
2
3
4
union test{
int a;
int * next;
}
这个union结构体会占用8字节(64位操作系统)。当分配出去的时候,8个字节用来储存对象。当空闲的时候,前四个字节用来储存指针。
这样的指针我们叫做嵌入式指针
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。