虚拟内存的作用是什么 虚拟内存有什么用( 七 )


分配器以块为单位来维护堆 , 可分配或释放 。 有两种类型的分配器:
显式分配器:应用分配并且回收空间(C 语言中的 malloc 和 free)
隐式分配器:应用只负责分配 , 但是不负责回收(Java 中的new和垃圾收集)
7.1 相关函数malloc:失败返回空指针并设置errno=ENOMEM , 成功返回指向至少请求字节大小的内存块的指针
free:将参数p指向的块返回到可用内存池 , p必须来自先前调用的malloc、calloc、realloc
calloc:将已分配的块初始化为0的malloc版本
realloc:改变先前分配的快的大小
sbrk:分配器内部使用 , 用于增加或缩小堆
先来看看一个简单的使用 malloc 和 free 的例子
#include <stdio.h>#include <stdlib.h>void foo(int n) { int i, *p; /* Allocate a block of n ints */ p = (int *) malloc(n * sizeof(int)); if (p == NULL) { perror("malloc"); exit(0); } /* Initialize allocated block */ for (i=0; i<n; i++) p[i] = i; /* Return allocated block to the heap */ free(p);}为了讲述方便 , 我们做如下假设:
内存地址按照字来编码
每个字的大小和整型一致
例如:

虚拟内存的作用是什么  虚拟内存有什么用

文章插图

可以用任意的顺序发送 malloc 和 free 请求 , 但free 请求必须作用与已被分配的 block 。
显式分配器的限制:
不能控制已分配块的数量和大小
必须立即响应 malloc 请求(不能缓存或者给请求重新排序)
必须在未分配的内存中分配
不同的块需要对齐(32 位中 8 byte , 64 位中 16 byte)
只能操作和修改未分配的内存
不能移动已分配的块 , 不允许压缩 , 为什么?
7.2 性能指标假设给定 malloc 和 free 的请求的序列:
R0,R1,…,Rk,…,Rn?1R0,R1,…,Rk,…,Rn?1
目标是尽可能提高吞吐量以及内存利用率(注意 , 这两个目标常常是冲突的)
吞吐量是在单位时间内完成的请求数量 。 假设在 10 秒中之内进行了 5000 次 malloc 和 5000 次 free 调用 , 那么吞吐量是 1000 operations/second
另外一个目标是 Peak Memory Utilization , 就是最大的内存利用率 。 其次最小化开销 , 定义总负载Pk表示请求Rk完成时的当前已分配的载荷之和 , 当前堆大小Hk(假定Hk是单调非递减) , 则K+1次请求的开销Ok = Hk / (maxi≤k P i ) – 1.0
虚拟内存的作用是什么  虚拟内存有什么用

文章插图

7.3 内存碎片影响内存利用率的主要因素就是『内存碎片』 , 分为内部碎片和外部碎片两种 。
内部碎片
内部碎片指的是存储的数据(payload)小于块大小 , 就会因对齐和维护堆所需的数据结构或明确的决策(如返回大块满足小的请求)的缘故而出现无法利用的空间 , 例如:
虚拟内存的作用是什么  虚拟内存有什么用

文章插图

内部碎片只依赖于之前的请求的具体模式
外部碎片
内存中有足够的空间 , 但是空间不连续 , 所以成为了碎片 , 取决于未来请求的模式
虚拟内存的作用是什么  虚拟内存有什么用

文章插图

实现细节如何实现高效的内存分配算法?在具体实现之前 , 需要考虑以下问题:
给定一个指针 , 如何知道需要释放多少内存?
如何记录未分配的块?
当分配的结构小于所在的空闲块时 , 如何处理多余的空间?
有多个区域满足条件 , 如何选择?
如何重用已经释放的块?
如何实现回收
标准方法:在块开头记录块的长度(包括块头) , 每个分配的块需额外的字节