本文目的
在C++内存分配方面,会见到很多操作:new、delete、new[]、delete[]、operator new、 operator delete、malloc、 free、 STL里的 allocator、deallocator,甚至linux内核中的 slab allocator、kmalloc、vmalloc。 种类繁多,眼花缭乱。本文就大概捋一下整体的关系。
内存分配体系
图中是相对明了的,但只是大概的层级关系。还有些特殊情况。
- 在我们编写程序时,之前提的所有的分配(系统调用视系统而定)都可以拿来直接用,但是它们之中又分了很多层级。
- STL内有自己的allocator,但是有很多种,有些是简单的封装调用了operator new,有些则实现了一个内存池(STL源码剖析里的alloc),而内存池需要内存时根据malloc获取内存。
- 我们经常用的new内部是调用operator new 和对象的构造函数。
- 全局的operator new 调用了malloc 并且还有malloc失败时的new_handle函数(可以设定)。(operator new可以重载,一般有需求会在类内重载。重载全局的operator new有一定风险,就不细说了)
- malloc在不同库中会有不同的实现。glibc中是ptmalloc2,其他库还有各种比较好的实现。并且基本上malloc实现又是一种内存池(比之前提的alloc,要复杂的多,毕竟malloc作用范围比alloc要广的多,也是很多有实力的技术团队在不断研究改进的成果)
- 在linux里,malloc底层又调用了系统内的brk和mmap来分配内存。
- kmalloc和vmalloc则是linux内核中实现,kmalloc分配物理地址连续且虚拟地址连续的内存,vmalloc分配物理地址不连续,虚拟地址连续的内存。所以vmalloc会慢(与总线协议,DDR硬件本身的特性有关(访问不同块需要预充电等))
- kmalloc则是从slab allocator获取内存,而slab则又是一种内存池结构。
本文只是粗略的概述了大部分内存分配函数或操作的关系,每种自身又有很多可以深挖的细节。想把它们理清楚,是需要几本书才可以的。特别是不同库中malloc的实现(对于多线程的优化手段,为了分配和回收效率的复杂结构设计,回收的内存碎片的合并技巧,等等)
尾语
内存分配是C++程序员不得不了解的知识,尤其是当需要实现自己的内存池时,必须要知道底层已经做了哪些工作了,我们在上层写的内存池还能做哪些工作,还需要做哪些工作。不然,很可能你实现的功能,底层已经帮你做了,而你所做的效率可能还不如底层分配的效率,无谓而且有害。
以上