本文包含 primitives(基础工具),malloc / free,std::allocator,other allocators,loki::allocator的底部动作介绍,从最基础的C++语言构件到高知名度的内存管理器
视频学习:侯捷老师《内存管理——从平地到万丈高楼》 资源网站 道格·利亚的工作站
A Memory Allocator
Bibliography(书目志) 《STL源码剖析》by 侯捷 Chap2:allocator
《Small Memory Software》by James Noble & Charles Weir,AW 2001
《Modern C++ Design》by Andrei Alexandrescu Chap4:Small-Object Allocation
Libraries(库)
STL Allocators
MFC CPlex + CFixedAlloc
Boost.Pool
Loki SmallObjAllocator
VC malloc / free
jemalloc
tcmalloc
C++应用程序
C++ memory primitives(基础工具)
分配
释放
类属
可否重载
malloc()
free()
C函数
不可
new
delete
C++表达式(expressions)
不可
::operator new()
::operator delete()
C++函数
可
allocator<T>::allocate()
allocator<T>::deallocate()
C++标准库
可自由设计并以之搭配任何容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 void * p1 = malloc (512 ); free (p1);complex<int >* p2 = new complex<int >; delete p2;void * p3 = ::operator new (512 ); ::operator delete (p3) ; #ifdef _MSC_VER int * p4 = allocator <int >().allocate (3 , (int *)0 ); allocator <int >().deallocate (p4, 3 ); #endif #ifdef __BORLANDC__ int * p4 = allocator <int >().allocate (5 ); allocator <int >().deallocate (p4, 5 ); #endif #ifdef __GNUC__ void * p4 = alloc::allocate (512 ); alloc::deallocate (p4, 512 ); #endif #ifdef __GNUC__ void * p4 = allocator <int >().allocate (7 ); allocate <int >().deallocate ((int *)p4, 7 ); void * p5 = __gnu_cxx::__pool_alloc<int >().allocate (9 ); __gnu_cxx::__pool_alloc<int >().deallocate ((int *)p5, 9 ); #endif
new expression 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Complex* pc = new Complex (1 , 2 ); Complex* pc; try { void * mem = operaotr new (sizeof (Complex)); pc = static_cast <Complex*>(mem); pc->Complex::Complex (1 , 2 ); }catch (std::bad_alloc){ } void * operator new (size_t size, const std::nothrow_t &) _THROW0 () { void * p; while ((p = malloc (size)) == 0 ) { _TRY_BEGIN if (_callnewh(size) == 0 ) break ; _CATCH(std::bad_alloc) return (0 ); _CATCH_END } return (p); }
delete expression 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Complex* pc = new Complex (1 , 2 ); ... delete pc;pc->~Complex (); operator delete (pc) ; void __cdecl operator delete (void * p) _THROW0 () { free (p); }
Ctor & Dtor 直接调用 1 2 3 4 5 6 string* pstr = new string; cout << "str= " << *pstr << endl; cout << "str= " << *pstr << endl;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class A { public : int id; A (int i) : id (i) { cout << "ctor. this=" << this << " id=" << id << endl; } ~A () { cout << "dtor. this=" << this << endl; } }; A* pA = new A (1 ); cout << pA->id << endl; cout << pA->id << endl; delete pA;
array new, array delete 1 2 3 4 5 Complex* pca = new Complex[3 ]; ... delete [] pca;
cookie 记录整块长度,内存泄漏不发生在内存块回收,发生在 dtor(调用次数),因此对默认析构函数的类使用 delete pca,数组本身不会泄漏,泄漏的是指针指向的空间
没对每个 object 调用 dtor,有什么影响 对 class without ptr member 可能没影响 对 class with pointer member 通常有影响(内存泄漏)
1 2 3 string* psa = new string[3 ]; ... delete psa;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class A { public : int id; A () : id (0 ) { cout << "default ctor. this=" << this << "id=" << id << endl; } A (int i) : id (i) { cout << "ctor. this=" << this << "id=" << id << endl; } ~A () { cout << "dtor. this=" << this << "id=" << id << endl; } }; A* buf = new A[size]; A* tmp = buf; cout << "buf=" << buf << "tmp=" << tmp << endl; for (int i = 0 ; i < size; ++i) new (tmp++)A (i); cout << "buf=" << buf << "tmp=" << tmp << endl; delete [] buff;
array size, in memory block 1 2 3 4 5 6 int * pi = new int [10 ]; delete pi; int ia[10 ]; cout<<sizeof (ia);
橙色部分只有在 Debugger mode 下出现,大小确定(32个字节和4个字节) pi 指向第一个 int 数据即 00441c30 位置 两个 cookie,上下各一个,记录整块大小,cookie 最后一个比特被借用来当一个 on/off 的状态(是否给出去) pad 用于边界对齐,VC6 要调整到16的边界
1 2 3 4 5 6 7 Demo* p = new Demo[3 ]; delete [] p; Demo d[3 ]; cout << sizeof (d);
如果放的是 object,其析构函数是重要的(即非默认析构函数),那么编译器在创建一个 array 时就不一样,如上面的 Demo,会把3(数组大小)写入内存块
placement new
placement new 允许我们将 object 建构于 allocated memory 中
没有所谓 placement delete,因为 placement new 根本没分配 memory 亦或称呼于 placement new 对应的 operator delete 为 placement delete
注意,关于“placement new”,或指 new(p),或指 ::operator new(size, void*)(两个参数版本,不做事)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <new> char * buf = new char [sizeof (Complex)*3 ]; Complex* pc = new (buf)Complex (1 , 2 ); ... delete [] buf;Complex* pc; try { void * mem = operator new (sizeof (Complex), buf); pc = static_cast <Complex*>(mem); pc->Complex::Complex (1 , 2 ); }catch (std::bad_alloc) { } void * operator new (size_t , void * loc) { return loc; }
C++应用程序,分配内存的途径
C++容器,分配内存的途径
重载 ::operator new / ::operator delete 重载全局 operator new 和 operator delete,小心,这影响无远弗届(没有不能到达的地方)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void * myAlloc (size_t size) { return malloc (size); }void myFree (void * ptr) { return free (ptr); }inline void * operator new (size_t size) { cout << "star global new() \n" ; return myAlloc (size); }inline void * operator new [](size_t size){ cout << "star global new[]() \n" ; return myAlloc (size); } inline void operator delete (void * ptr) { cout << "star global delete() \n" ; myFree (ptr); }inline void operator delete [](void * ptr){ cout << "star global delete[] \n" ; myFree (ptr); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void * operator new (size_t size, const std::nothrow_t &) _THROW0 () { void * p; while ((p = malloc (size)) == 0 ) { _TRY_BEGIN if (_callnewh(size) == 0 ) break ; _CATCH(std::bad_alloc) return (0 ); _CATCH_END } return (p); } void __cdecl operator delete (void * p) _THROW0 () { free (p); }
重载 operator new / operator delete 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Foo* p = new Foo; try { void * mem = operator new (sizeof (Foo)); p = static_cast <Foo*>(mem); p->Foo::Foo (); } delete p;p->~Foo (); operator delete (p) ;class Foo {public : void * operator new (size_t ) ; void operator delete (void *, size_t ) ; };
重载 operator new[] / operator delete[] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Foo* p = new Foo[N]; try { void * mem = operator new (sizeof (Foo)*N + 4 ); p = static_cast <Foo*>(mem); p->Foo::Foo (); } delete [ ] p;p->~Foo (); operator delete (p) ;class Foo {public : void * operator new [](size_t ); void operator delete [](void *, size_t ); };
示例,接口