示例1:在类中提前分配一块连续的内存池,减少cookie(分配内存时产生的头尾共8个字节,用于记录分配了多少内存)对内存的消耗
class Screen {
public:
Screen(int x) : i(x) {}
int getData() { return i; }
void* operator new(size_t);
void operator delete(void*, size_t);
private:
static Screen* freeStore;//对象的内存地址
static const int screenChunk;//要获取的Screen类的个数,内存数量 = sizeof(Screen) * screenChunk
Screen* next;
int i;
};
Screen* Screen::freeStore = nullptr;
const int Screen::screenChunk = 4;
void* Screen::operator new(size_t size){
Screen* p;
//freeStore不为空表示预先拿到的内存还有位置
if (!freeStore) {
size_t chunk = size * screenChunk;
freeStore = p = reinterpret_cast(new char[chunk]);
for (; p != &freeStore[screenChunk - 1]; ++p)
p->next = p + 1;
p->next = 0;
}
p = freeStore;
freeStore = freeStore->next;//每调用一块内存往后移一位
return p;
}
void Screen::operator delete(void* p, size_t size) {
static_cast(p)->next = freeStore;
freeStore = static_cast(p);
}
void test() {
cout << "sizeof Screen: " << sizeof(Screen) << endl;//获取Screen大小
Screen* p[100];
for (int i = 0; i < 100; i++) {
p[i] = new Screen(i); //创建
}
for (int i = 0; i < 100; i++) {
cout << "address: " << p[i] << " i = " << p[i]->getData() << endl;
}
for (int i = 0; i < 100; i++) {
delete p[i]; //销毁
}
}
结果:screenChunk取值为4,所以每4个Screen对象的空间是连续的
示例2:例1的优化版,加入embedded pointer
class Airplane {
private:
struct AirplaneRep {
unsigned long miles;
char type;
};
union {
AirplaneRep rep;
Airplane* next;
};
public:
unsigned long getMiles() { return rep.miles; }
char getType() { return rep.type; }
void set(unsigned long m, char t) {
rep.miles = m;
rep.type = t;
}
void* operator new(size_t size);
void operator delete(void*, size_t);
private:
static Airplane* headOfFreeList;
static const int BLOCK_SIZE;
};
Airplane* Airplane::headOfFreeList = nullptr;
const int Airplane::BLOCK_SIZE = 512;
void* Airplane::operator new(size_t size){
if (size != sizeof(Airplane)) {
return ::operator new(size);
}
Airplane* p = headOfFreeList;
if (p) headOfFreeList = p->next;
else {
size_t total_size = size * BLOCK_SIZE;
Airplane* newBLOCK = reinterpret_cast(new char[total_size]);
for (int i = 1; i < BLOCK_SIZE-1; i++)
newBLOCK[i].next = &newBLOCK[i + 1];
newBLOCK[BLOCK_SIZE - 1].next = 0;
p = newBLOCK;
headOfFreeList = &newBLOCK[1];
}
return p;
}
void Airplane::operator delete(void* deadObject, size_t size) {
if (deadObject == 0) return;
if (size != sizeof(Airplane)) {
::operator delete(deadObject);
return;
}
Airplane* carcass = static_cast(deadObject);
carcass->next = headOfFreeList;
headOfFreeList = carcass;
}
void test() {
const int size = 100000;
cout << "sizeof Airplane: " << sizeof(Airplane) << endl;//获取Screen大小
Airplane* p[size];
for (int i = 0; i < size; i++) {
p[i] = new Airplane();
p[i]->set(i, i % 3);
}
for (int i = 0; i < size; i++) {
cout << "address: " << p[i] << " i = " << p[i]->getMiles() << endl;
}
for (int i = 0; i < size; i++) {
delete p[i];//销毁
}
}
示例3:采用一个类来进行内存池,让所有类都能共用
示例4:加入宏定义
示例5:标准库pool_allocator的实现
//******** pool_allocator 设计 ********
enum { __ALIGN = 8 };
enum { __MAX_BYTES = 128 };
enum { __NRFEELISTS = __MAX_BYTES / __ALIGN };
template
class __default_alloc_template {
private:
//向上取8倍数
static size_t ROUND_UP(size_t bytes) {
return (((bytes) +__ALIGN - 1) & ~(__ALIGN - 1));
}
private:
union obj {
union obj* free_list_link;
};
private:
static obj* volatile free_list[__NRFEELISTS];//存入16条free_list的头
static size_t FREELIST_INDEX(size_t bytes) { //根据obj大小找到对应的free_list索引
return (((bytes) +__ALIGN - 1) / __ALIGN - 1);
}
//分配内存池中的内存
static void* refill(size_t n);
//向系统获取内存放入内存池
static void* chunk_alloc(size_t size, int& nobjs);
static char* start_free; //内存池起点
static char* end_free; //内存池终点
static size_t heap_size; //从系统中获取的内存大小
public:
static void* allocate(size_t n) {
obj* volatile *my_free_list;
obj* result;
if (n > __MAX_BYTES) {
return ::operator new(n);
}
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
if (0 == result) {
void* r = refill(ROUND_UP(n));
return r;
}
*my_free_list = result->free_list_link;
return result;
}
static void deallocate(void* p, size_t n) {
obj* q = (obj*) p;
obj* volatile *my_free_list;
if (n > __MAX_BYTES) {
::operator delete(q, n);
return;
}
my_free_list = free_list + FREELIST_INDEX(n);
q->free_list_link = *my_free_list;
*my_free_list = q;
}
//先不实现
static void* reallocator(void* p, size_t old_sz, size_t new_sz);
};
template
void* __default_alloc_template::
chunk_alloc(size_t size, int& nobjs) {
char* result;
size_t total_bytes = size * nobjs;
size_t bytes_left = end_free - start_free;
//内存池够取需要的大小
if (bytes_left >= total_bytes) {
result = start_free;
start_free += total_bytes;
return result;
}
//内存池够一个或以上的obj大小
else if(bytes_left >= size){
nobjs = bytes_left / size;
total_bytes = nobjs * size;
result = start_free;
start_free += total_bytes;
return result;
}
//内存池连一个都obj大小都分不出来,先把剩余的内存分出去,再向系统要内存
else {
size_t bytes_to_get = 2 * total_bytes + (ROUND_UP(heap_size >> 4));
//内存池还有内存,先把剩下的内存分出去
if (bytes_left > 0) {
obj* volatile *my_free_list = free_list + FREELIST_INDEX(bytes_left);
((obj*) start_free)->free_list_link = *my_free_list;
*my_free_list = (obj*) start_free;
}
//向系统要内存
start_free = (char*) malloc(bytes_to_get);
//start_free == 0 意味着系统分不出这么多内存了,需要向free_list里比size大的list要内存
if (0 == start_free) {
size_t i;
obj* volatile *my_free_list;
obj* p;
for (i = size; i <= __MAX_BYTES; i + __ALIGN) {
my_free_list = free_list + FREELIST_INDEX(i);
p = *my_free_list;
//要到了
if (nullptr != p) {
*my_free_list = p->free_list_link;
start_free = (char*) p;
end_free = start_free + i;
return chunk_alloc(size, nobjs);
}
}
//没要到,程序跑到这里应该会陷入死循环
end_free = 0;
start_free = (char*) ::operator new(bytes_to_get);
}
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
return chunk_alloc(size, nobjs);
}
}
template
void* __default_alloc_template::
refill(size_t n) {
int nobjs = 2;//一般为20,为方便实验,每次取2块obj的大小
char* chunk = (char*) chunk_alloc(n, nobjs);
obj* volatile *my_free_list;
obj* result;
obj* current_obj;
obj* next_obj;
int i;
if (1 == nobjs) return chunk;
my_free_list = free_list + FREELIST_INDEX(n);
//在chunk内建立free_list
result = (obj*) chunk;
*my_free_list = next_obj = (obj*) (chunk + n);
for (i = 1; ; ++i) {
current_obj = next_obj;
next_obj = (obj*) ((char*)current_obj + n);
if (i == nobjs - 1) {
current_obj->free_list_link = 0;
break;
}
else {
current_obj->free_list_link = next_obj;
}
}
return result;
}
template
char* __default_alloc_template::start_free = 0;
template
char* __default_alloc_template::end_free = 0;
template
size_t __default_alloc_template::heap_size = 0;
template
typename __default_alloc_template::obj* volatile
__default_alloc_template::free_list[__NRFEELISTS]
= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
typedef __default_alloc_template myPoolAlloc;
void test() {
myPoolAlloc alloc;
int* pi[6];
//new
for (int i = 0; i < 6; ++i) {
pi[i] = (int*) alloc.allocate(16);
cout << pi[i] << endl;//打印地址
}
//delete
for (int i = 0; i < 6; ++i) {
alloc.deallocate(pi[i],16);
}
}
结果:每次取2个区块,第一次内存池获取两倍的所需内存,所以应该有四块内存连续,结果正确
size_t bytes_to_get = 2 * total_bytes + (ROUND_UP(heap_size >> 4));