目录
前言
本篇是学习记录,比较冗杂,快速掌握大概,可阅读精简版
第一讲 primitives
1、C++ memory primitives
2、四个层面的基本用法
malloc
new/delete
::operator new/::operator delete
C++标准库提供的allocators
- gunc为2.9版本
- gunc 4.9的其中两个分配器
调用顺序
3、new/delete expression
new expression
# code a
Complex* pc=new Complex(1,2);
# code b
Complex* pc;
try{
void* mem =operator new(sizeof(Complex));//allocate
pc=static_cast<Complex*>(mem);//cast
pc->Complex::Complex(1,2);//construct,只有编译器可以这样调用
}
catch(std::bad alloc){
//若allocation失败就不软行constructor
}
delete expression
Ctor&Dtor (构造函数和析构函数)直接调用
不能直接调用构造函数和析构函数。因为虽然直接调用析构函数可以编译通过,但是构造函数大部分情况下编译不通过,因此两个函数不能成对通过的话,代码运行会crash。
#欲直接用ctor,可用placement new:
new(p)Complex(1,2);
4、Array new/delete
使用方式
- cookie记录整块的长度
- 若使用array new 后,最后delete不加 [ ],cookie记录的长度并没有随之改变,因此上图所示绿色部分是不会有内存泄漏的,都会回收到操作系统,
内存泄露真正发生在调用析构函数的次数上
,如上例子所示,加[ ]会调用3次析构函数,不加[ ]只调用一次析构函数,但是由于complex object是复数,内部无指针,因此此时delete不加[ ],只调用一次析构函数就足够了,不需要回收其他地方的内存,只需要回收绿色部分,也不会发生内存泄露。
泄漏举例
- string内部有一根指针
- 若使用array new 后,最后delete不加 [ ],内存泄露发生在下图绿色部分三个箭头指向的地方
5、placement new
6、C++分配内存的途径
应用程序
容器
重载::operator new/::operator delete
- vc6源码
- 重载
重载operator new/operator delete
重载operator new[]/operator delete[]
重载new()/delete()
- 重载 placement new总结
- 例子
7、Per-class allocator
小型内存管理
- 减少malloc调用次数
- 降低cookie用量
- 内存池提升速度,降低浪费率
匿名union的使用
- note:
//定义union,
//第一种方式:定义了一个union类型,并且定义了一个属于该类型的对象un,因此sizeof(X)=4
class X{
public:
union UN{
int m_nX;
char* pchar;
}un;
};
//第二种方式:定义了一个union类型,但并没有定义属于该类型的任何对象,
//因此理论上sizeof(X)=0,但实际上sizeof(X)=1
class X{
public:
union UN{
int m_nX;
char* pchar;
};
};
//第三种方式://定义了一个匿名的union类型,因此也就只能在X内部使用了,
//并且此种情况隐含着已经在X中定义了一个属于该匿名union类型的象,
//因此sizeof(X)=4,并且此种情况最为特殊,可以直接通过类X的对象来使用m_nX和pchar,
//例如X x;x.m_nX=10;x.pchar="Hello World!";
class X{
public:
union {
int m_nX;
char* pchar;
};
};
-
对于前两种方式,都在类的内部定义了一个新的具有名字的型别,因此可以用这种新的类型来定义对应于该类型的对象,例如:
X::UN myun;
但是这种定义是要求类内部的union在定义时的修饰符为public,否则就不能在类外部定义属于他们的对象,而只能在给类X内部使用了。同理也可以知道在类内部通过typedef定义出来的新型别也是如此。 -
需要说明的是,union用类里以第三种定义的方式出现的话,会有很奇妙的效果,非常奇妙。m_nX和pchar共享一块4个字节的内存,只要不是同时需要m_nX和pchar出现的情况都可以只利用它们其中的一个,从而节省了内存开销。
static allocator
营你受困於必须为不同的 classes 重寓一遍乎相同的member operator new 和member operator delete时,应该有方法将一個是分配特定尽寸之区块的memory allocator概念包装起来,使它容易被重使用。使得每个allocator object 都是個分配器,它体内维护一個free-lists;不同的allocator objects维不同的free-lists。
8、new handler
nothrow形式
当operator new没能力为你分配出你所申请的memory,会抛一個std::bad_alloc exception。某些老版编译器则是返回0,你仍然可以令编译器那麽做:new(nothrow)Foo;//此种为nothrow形式
new handler
抛出exception之前先(不只一次)调用一個可由 client指定的handler,以下是new handler的形式和定方法:
- 设计良好的new handler只有两个选择:
- 镶更多memory可用
- 调用abortO或exitO
9、=default,=delete
第二讲 std::allocator
1、各主流编译器的标准分配器
1、vc6 标准分配器
2、BC5 标准分配器
3、G2.9 标准分配器
- 但是G2.9容器使用的分配器,不是std::allocator 而是std::alloc
4、G4.9 标准分配器
- G4.9 中的_pool_alloc去除了cookie(8字节)
2、 G2.9 std::alloc运行模式
特点
- freelist管理16种大小的链表
- 超过alloc负责的大小会交给malloc处理
- 被要求分配内存不是8的倍数,会被调整为8的倍数
- 每次分配40个,实质使用20个,剩余20个备用,下次分配内存将会切割这部分备用的20个
内嵌指针embedded pointers
详细过程
- 1
- 2
- 3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
- 11(修改系统内存至1 0000)
- 12
- 13
- 思考
G2.9 std::alloc源码剖析
G2.9 std::alloc小应用
- G2.9的容器默认使用std::alloc
第三讲 malloc/free
1、vc6和vc10的malloc比较
- vc6的SBH,在vc10中被放到了heapalloc()中
2、vc6内存分配
- _heap_init(…)分配16个头
- _ioinit()第一次内存分配,256字节(100h)
- _sbh_alloc_block()将大小调整为16的倍数
- _sbh_alloc_new_region()每个头申请内存,并添加两根指针,一个指向管理单元,一个指向可用内存(1M,虚的)
- sbh_alloc_new_group()可用内存切片
- _crtGetEnvironmentStringsA第二次申请内存并分配
分配
释放
再分配
3、内存管理之区域合并
- 上下cookie的存在,方便了 两个相邻区域内存的 合并回收
4 、总结
- 将内存分层管理,更有利于归还内存给操作系统,因为块越小,遇到回收的概率越大
- 判断全回收:通过GROUP的cntEntnes的值来判断。若为0,则可全回收
- defering推迟回收:
- _sbh_pHeaderDefer是個指针,指向一個全回收 group所属的Header。这個group原本應被释放,但暂保留(延缓释放)。当再有第二個全回收 group出现,SBH才释放道個Defer group,并将新出现的全回收 group般为Defer。
- 如果尚未出现第二個全回收group而又从Defer group取出block完成分配,Defer指针会被取消(设为NULL)
- _sbh_indGroupDefer是個索引,指出Region中哪個 group(#0-#31)是Defer.
- 当你归还(释放)所有内存块,SBH系统将呈现什麽面貌?初始状态
- VC malloc 和GCC allocator
第四讲 loki::allocator
- gnuc的分配器致命伤就是非常霸道,不归还内存,但是loki的分配器可以还
第五讲 other allocators
1、GNU C++对于各allocator的描述
2、VS2013标准件分配器
- 平淡如水,仍然调用::operator new()和::operator delete(),实际上什么都没做
3、G4.9标准分配器
4、G4.9 malloc_allocator
5、G4.9 array_allocator
- array_allocator的使用
- 先做一个数组,然后使用array_allocator分配内存
或者 - 使用new分配一块动态内存,然后使用array_allocator
6、G4.9 debug_allocator
7、G4.9 _pool_alloc
- G4.9 _pool_alloc就是G2.9的alloc的化身(第二讲的那种分配器),特点就是只拿不还
- 用例
8、G4.9 bitmap_allocator
- bitmap_allocator宏观
- blocks,super-blocks,bitmap,mini-vector简介
- bitmap_allocator分配内存过程
- 用了一块
- 用了63块
- 还回去一块
- 如果第一块用光,启动第二块
- 如果第二块用光,启动第三块
- bitmap_allocator回收内存过程
- 第1个super_block全回收
- 第2个super_block全回收
- 第3个super_block全回收
9、使用G4.9分配器
第六讲 其他
1、谈谈const
- const后置只能加在类成员函数身上,不能加在全局函数身上
2、关于new、delete
重载 ::operator new,::operator delete
::operator new[],::operator delete[]
重载member operator new/delete
重载member operator new[]/delete[]
示例
重载new(),delete()
- placement new