程序中的数据和变量都会被分配到程序所在的虚拟内存中,内存空间包含两个重要区域:栈区(Stack)和堆区(Heap)。函数调用的参数、返回值以及局部变量大都会被分配到栈上,这部分内存会由编译器进行管理;不同编程语言使用不同的方法管理堆区的内存,C++ 等编程语言会由使用者主动申请和释放内存,Go 以及 Java 等编程语言会由使用者和编译器共同管理,堆中的对象由内存分配器分配并由垃圾收集器回收。
Golang的内存分配器借鉴了 tcmalloc 的思想,尽量减少在多线程模型下,锁的竞争开销,来提高内存分配的效率。
线性分配(Bump Allocator)是一种高效的内存分配方法,但是有较大的局限性。当我们使用线性分配器时,只需要在内存中维护一个指向内存特定位置的指针,如果用户程序向分配器申请内存,分配器只需要检查剩余的空闲内存、返回分配的内存区域并修改指针在内存中的位置,即移动下图中的指针:
空闲链表分配器(Free-List Allocator)可以重用已经被释放的内存,它在内部会维护一个类似链表的数据结构。当用户程序申请内存时,空闲链表分配器会依次遍历空闲的内存块,找到足够大的内存,然后申请新的资源并修改链表:
tcmalloc,其实就是 thread cache malloc 的缩写。首先,看一下下图也大概了解一下tcmalloc
- tcmalloc 内存分配分为ThreadCache、CentralCache、PageHeap 三个层次。