用malloc申请的内存中会保存此次申请的大小及相关调试cookie,这些信息在连续申请内存时是多余的,因为申请的每个对象的内存大小都一样;malloc会调用系统调用向操作系统申请内存,这涉及到上下文切换。所以我们内存管理的目的:
operator new/operator delete
原型 void* operator new(size_t size) // size为要申请的内存大小(字节) void operator delete(void* deadObj, size_t size)
malloc/free
void* malloc(size_t size) void free(void* deadObj)
placement new/placement delete
注意:placement new并不会申请内存,只是在已申请的内存上做构造函数的操作,只有placement new调用失败时,才会调用相应的placement delete来处理构造失败的情况
使用 void* p = operator new(sizeof(int)); int* pi = static_cast<int*>(p); new(p)int(10);
struct Foo{ Foo(int _i):i(_i){} int i; } int* p = new Foo(10);
int* p = new Foo(10) 背后的机制
1. void* mem = operator new(sizeof(Foo)); 2. Foo* pc = static_cast<Foo*>(mem); 3. pc->Foo::Foo(10); // vc下可以这么调用,gnuc会失败 new(pc)Foo(10) // 也可用placement new操作
operator new操作具体的步骤
主要的动作为malloc, 当malloc失败时,也就是oom,调用自己的new handler处理失败的场景,可能的实现,释放暂时用不到的内存,让本次调用返回可用内存。
new handler具体的目的有两个1.让跟多的内存可用2.调用abort()或exit()
class Screen { public: Screen(int x) :i(x) {} int get() { return i; } void* operator new(size_t); void operator delete(void*, size_t); private: Screen* next; static Screen* freeStore; static const int screenChunk; private: int i; }; void* Screen::operator new(size_t size){ Screen* p; if (!freeStore) { size_t chunk = screenChunk*size; // 申请一大块内存 freeStore = p = reinterpret_cast<Screen*>(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) { (static_cast<Screen*>(p))->next = freeStore; freeStore = static_cast<Screen*>(p); } Screen* Screen::freeStore = 0; const int Screen::screenChunk = 24;
改进:加入embedded pointer 节省next指针的开销,因为next指针只有在分配内存和回收内存的时候会使用,而返回给用户时只会使用该对象的内容,而不会使用next指针,所以用union包裹next指针和类对象
class Airplane { private: struct AirplaneRep { unsigned long miles; char type; }; private: 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; } public: // 这里加不加static都可以,编译器会给我们加上,因为类对象未构造,其行为未知。 static void* operator new(size_t size); static void operator delete(void* deadObj, size_t size); private: static const int BLOCK_SIZE; static Airplane* headOfFreeList; }; Airplane* Airplane::headOfFreeList; // 空闲链表头 const int Airplane::BLOCK_SIZE = 512; // 空闲链表为空时,一次申请的块数 void* Airplane::operator new(size_t size){ // 继承会导致size不等,这里不做过多考虑 if (size != sizeof(Airplane)) return ::operator new(size); Airplane* p = headOfFreeList; if (p) headOfFreeList = p->next; else { Airplane* newBlock = static_cast<Airplane*> (::operator new(BLOCK_SIZE*sizeof(Airplane))); // 将申请的内存用链表串起来 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* deadObj, size_t size) { if (deadObj == 0) return; if (size != sizeof(Airplane)) { ::operator delete(deadObj); return; } //回收该对象,指针的next指向空闲链表头, 然后调整空闲链表头部为deadObj (static_cast<Airplane*>(deadObj))->next = headOfFreeList; headOfFreeList = static_cast<Airplane*>(deadObj); }
给没一个类配置一个allocator
class myAllocator { private: struct obj { struct obj* next; //embedded pointer }; public: void* allocate(size_t); void deallocate(void*, size_t); //void check(); private: obj* freeStore = nullptr; const int CHUNK = 5; //小一點方便觀察 }; void* myAllocator::allocate(size_t size) { obj* p; if (!freeStore) { //linked list 是空的,所以攫取一大塊 memory size_t chunk = CHUNK * size; freeStore = p = (obj*)malloc(chunk); //cout << "empty. malloc: " << chunk << " " << p << endl; //將分配得來的一大塊當做 linked list 般小塊小塊串接起來 for (int i = 0; i < (CHUNK - 1); ++i) { //沒寫很漂亮, 不是重點無所謂. p->next = (obj*)((char*)p + size); p = p->next; } p->next = nullptr; //last } p = freeStore; freeStore = freeStore->next; //cout << "p= " << p << " freeStore= " << freeStore << endl; return p; } void myAllocator::deallocate(void* p, size_t) { //將 deleted object 收回插入 free list 前端 ((obj*)p)->next = freeStore; freeStore = (obj*)p; }
myAllocator的使用
class Foo { public: long L; string str; static myAllocator myAlloc; // Macro public: Foo(long l) : L(l) { } static void* operator new(size_t size) // Macro { return myAlloc.allocate(size); } static void operator delete(void* pdead, size_t size) // Macro { return myAlloc.deallocate(pdead, size); } }; myAllocator Foo::myAlloc; // Macro
将version3中的可重复使用的代码,写成宏即可
一级分配器(>128字节),直接调用malloc
二级分配器(<=128字节),调用alloc中重载的operator new()
解释:
假设申请对象大小为n。