8.1.12 边界标签缺点导致内部碎片 , footer标签能被优化 , 未使用一位代表已分配/未分配 , 除次之外使用多一位表示前一块是否分配
文章插图
也有四种情况 , 这里以第一种情况为例:
文章插图
8.2 显式空闲列表维护空闲列表块 , 而不是所有块 , 下一个/上一个空闲块可能在任何地方 , 所以需存储前向/后向指针 , 而不仅是块大小 , 合并仍需边界标签 , 根据记忆顺序找到相邻的块
文章插图
文章插图
8.2.1 释放块在空闲列表的什么地方放新释放的块?
无序:LIFO(Last in First out)、FIFO(First in First out) , 简单但花费时间 , 研究表明碎片化比按地址序更糟
地址序:插入空闲块 , 以便空闲块总按地址顺序排列 , 需搜索
同样对于释放内存块也有4种情况 , 这里以第四种情况做简要说明:
文章插图
8.2.2 一些实现的技巧建议使用循环双向链表 , 但数据结构支持多种方法 , 要么保持自由指针固定要么作为搜索列表移动
8.2.3 显式空闲列表总结分配是空闲块数量的线性时间 , 而不是所有块 。 由于需在列表中插入和取出块 , 因此分配和自由更复杂 。 但需要额外的链接空间
8.3 分割列表分配器每个大小块类都有自己的空闲列表
文章插图
8.3.1 分割分配器给定一个自由列表数组 , 每个都是一定大小的类 , 分配一个大小为n的块 , 搜索大小为m>n的块 , 若找到合适的块 , 分割块并放碎片在适当的列表 , 若没有块被发现 , 尝试下个更大的种类 。 重复直到块被找到 。 用sbrk从系统请求额外的堆内存 , 从该新内存分配n个字节的块 , 将剩余的块作为一个单独的空闲块放在适当大小的类中 。
8.3.2 分割分配器优点更高的吞吐量:种类大小的2次幂的对数时间vs线性时间
更好的内存利用率:对隔离的自由列表的First-fit搜索近似对整个堆的最佳搜索;极端情况是给每个块一个自己的大小的类相当于best-fit
8.4 内存陷阱关于内存的使用需要注意避免以下问题:
解引用错误指针
读取未初始化的内存
覆盖内存
引用不存在的变量
多次释放同一块
引用已释放的块
释放块失败
8.4.1 解引用错误指针没有引用对应的地址 , 少了 &
int val;...scanf("%d", val);
8.4.2 读取未初始化的内存不能假设堆中的数据会自动初始化为 0 , 如: /* return y = Ax */int *matvec(int **A, int *x) { int *y = malloc(N * sizeof(int)); int i, j; for (i = 0; i < N; i++) for (j = 0; j < N; j++) y[i] += A[i][j] * x[j]; return y;}
8.4.3 覆盖内存第一种是分配错误的大小 , 下面的例子中 , 一开始不能用 sizeof(int) , 因为指针的长度不一定和 int 一样 。 int **p;p = malloc(N * sizeof(int));for (i = 0; i < N; i++) p[i] = malloc(M * sizeof(int));
第二个问题是超出分配的空间 , 下面代码的 for 循环中 , 因为使用了 <= , 会写入到其他位置 int **p;p = malloc(N * sizeof (int *));for (i = 0; i <= N; i++) p[i] = malloc(M * sizeof(int));
第三种是因为没有检查字符串的长度 , 超出部分就写到其他地方去了(经典的缓冲区溢出攻击也是利用相同的机制)
- 什么是显卡 显卡有什么用
- 陀螺仪的作用是什么 陀螺仪有什么用
- 耳返的作用 耳返是干什么用的
- 电压互感器vv接线形式的特点
- 谐波保护器的应用领域及参数
- 数字解调器作用
- 调制解调器的用途与特点
- 调制器和解调器的作用
- 如何安装解调器
- 浅谈磨粉设备、输送设备与振动筛的配套问题