0%

内存管理

内存管理

Website

内存管理是一个很深刻的话题, 对于初学者来说, 内存管理看不到摸不着, 我们常使用 new / delete 来管理我们的堆内存. 仅此而已.

本文记录了侯捷老师内存管理课程的学习笔记以及部分自己写的代码.

目的是了解C++如何进行内存管理, 剖析源码, 能够设计自己的内存池.

C++中内存管理工具

分配 释放 类型 是否可以重载
malloc() free() C函数 不可以
new delete C++表达式 不可以
::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
void* p1 = malloc(512); //512 bytes
free(p1);

complex<int>* p2 = new complex<int>; //one object
delete p2;

void* p3 = ::operator new(512); //512 bytes
::operator delete(p3);

#ifdef __GUNC__ //GNUC 2.9

void* p4 = alloc::allocate(512);
alloc::deallocate(p4,512); //得记得当初申请了多少的内存,比较适用于容器

#endif

/*************************/
#ifdef __GNUC__ //GNUC 4.9

//allocate() 和 deallocate() 是 non-static 必须由 object 调用
void* p4 = allocator<int>().allocate(7); //分配7个int的内存大小
allocator<int>().deallocate((int*)p4, 7);

//allocate() 和 deallocate() 是 non-static 必须由 object 调用
void* p5 = __gnu_cxx::__pool_alloc<int>().allocate(9); //分配9个int的内存大小
__gnu_cxx::__pool_alloc<int>().deallocate((int*)p5, 9);

#endif

new 运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
Complex* pc = new Complex(1,2);

编译器转换→

Complex *pc;
try{
void* mem = operator new(sizeof(Complex)); //allocate
pc = static_cast<Complex*>(mem); //cast
pc->Complex::Complex(1,2); //construct
//只有编译器才能够像上式直接调用 ctor
} catch (std::bad_alloc) {
//allocate 失败, 不执行 ctor
}

operator new() vc98默认版本

1
2
3
4
5
6
7
8
9
10
11
12
void * operator new(size_t size, const std::nothrow t&) _THROW0()
{ //try to allocate size bytes
void *p;
while((p == malloc(size)) == 0){
//buy more memory or return null pointer
_TRY_BEGIN
if(_callnewh(size) == 0) break;
_CATCH(std::bad_alloc) return 0;
_CATCH_END
}
return (p);
}

delete 运算符

1
2
3
4
5
6
7
8
Complex* pc = new Complex(1,2);
...
delete pc;

编译器转换->

pc->~Complex(); //先析构
operator delete(pc); //然后释放内存

operator delete() vc98默认版本

1
2
3
4
void __cdecl operator delete(void * p) _THROW0()
{ //free an allocated object
free(p);
}

array new / array delete

1
2
3
4
Complex * pca = new Complex[3];
//触发三次ctor
...
delete [] pca; //触发三次dtor

内存分配的时候, 头部会有 cookie 方便回收.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int * pi = new int[10];
delete pi;

vc6 : cookie
61h(记录大小是60字节,1表示使用了这一块)
Debugger Header(32 Bytes)
int
int
int
...
int
no man land
Pad(12 Bytes)
61h

placement new

1
new (p)

允许我们在已经申请的堆内存上, 构建一个对象.

placement new 没有对应的 placement delete, 因为 placement new操作并没有分配内存.

1
2
3
4
5
6
7
8
9
10
11
12
13
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); //cast
pc->Complex::Complex(1,2); //construct
}

C++应用程序分配内存的途径

应用程序

1
2
3
4
5
6
7
8
9
10
Foo *p = new Foo(x);
delete p;

编译器->不可以改变不可以重载

Foo *p = (Foo*)operator new(sizeof(Foo));
new (p) Foo(x);

p->~Foo();
operator delete(x);

operator new / operator delete

1
2
3
4
5
6
7
Foo *p = (Foo*)operator new(sizeof(Foo));
调用 -> ::operator new(size_t);
调用 -> malloc(size_t);

operator delete(x);
调用 -> ::operator delete(void*);
调用 -> free(void*);

在类中重载 operator newoperator delete

1
2
3
4
5
Foo *p = (Foo*)operator new(sizeof(Foo));
重载 Foo::operator new(size_t); -> 调用 ::operator new(size_t);

operator delete(x);
重载 Foo::operator delete(void*); -> 调用 ::operator delete(void*);

C++容器分配内存的途径

容器

1
2
3
4
5
T *p = allocate();
construct();

destroy();
deallocate(p);

分配器

1
2
3
allocate();
deallocate();
调用 -> ::operator new or ::operator delete

重载 ::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
22
23
24
25
26
27
void * myAlloc(size_t size){
return malloc(size);
}

void myFree(void * ptr){
return free(ptr);
}

inline void * operator new(size_t size){
cout << "global new()" << endl;
return myAlloc(size);
}

inline void * operator new[](size_t size){
cout << "global new[]" << endl;
return myAlloc(size);
}

inline void operator delete(void * ptr){
cout << "global delete()" << endl;
myFree(ptr);
}

inline void operator delete[](void * ptr){
cout << "global delete[]" << endl;
myFree(ptr);
}

重载 operator new / operator delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Foo {
public:
/*重载这两个函数应该是 static, 编译器默认*/
void * operator new(size_t);
void operator delete(void *, size_t); //第二参数 optional
};

Foo *p = new Foo;
编译器->
try {
void * mem = operator new(sizeof(Foo)); //此处调用类中重载的 operator new
p = static_cast<Foo*>(mem);
p->Foo::Foo(1,2);
}

delete p; //使用 ::delete p; 可以绕过重载的 operator delete
编译器->
p->~Foo();
operator delete(p); //此处调用类中重载的 operator delete

重载 placement new / placement delete

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
class Foo {

/* 1,2默认版本; 3,4重载版本;*/

void * operator new(size_t size){ // 调用 new Foo
return malloc(size);
}

void * operator new(size_t size, void * start){ // 调用 new (&) Foo
return start;
}

void * operator new(size_t size, long extra){ // 调用 new (100) Foo
return malloc(size + extra);
}

void * operator new(size_t size, long extra, char init){ //调用 new(100,'a') Foo
return malloc(size + extra);
}

/*
** placement new 重载时, 第一参数必须为 size_t
** 否则, [Error] 'operator new' takes type 'size_t'(unsigned int)
** as first parameter
*/

/*
** placement delete 重载时, 不会被 delete 调用
** 除非 new 的时候抛出异常, 才会去调用对应的重载的 operator delete()
*/
};
-------------本文结束, 感谢您的阅读, 如有问题欢迎联系我-------------