一.这个项目做的是什么?
当前项⽬是实现⼀个⾼并发的内存池,他的原型是google的⼀个开源项⽬tcmalloc,tcmalloc全称 Thread-Caching Malloc,即线程缓存的malloc,实现了高效的多线程内存管理,⽤于替代系统的内存分配相关的函数(malloc、free)。
我们这个项目是把tcmalloc最核⼼的框架简化后拿出来,模拟实现出⼀个⾃⼰的⾼并发内存池,⽬的 就是学习tcamlloc的精华,这种⽅式有点类似我们之前学习STL容器的⽅式。但是相⽐STL容器部分, tcmalloc的代码量和复杂度上升了很多。
另⼀⽅⾯tcmalloc是全球⼤⼚google开源的,可以认为当时顶尖的C++⾼⼿写出来的,他的知名度也 是⾮常⾼的,不少公司都在⽤它,Go语⾔直接⽤它做了⾃⼰内存分配器。
二.项目需要掌握的技能
这个项⽬会⽤到C/C++、数据结构(链表、哈希桶)、操作系统内存管理、单例模式、多线程、互斥锁 等等⽅⾯的知识。
三.什么是内存池?
1.池化技术
所谓“池化技术”,就是程序先向系统申请过量的资源,然后⾃⼰管理,以备不时之需。之所以要申请过量的资源,是因为每次申请该资源都有较⼤的开销,不如提前申请好了,这样使⽤时就会变得⾮常快捷,⼤⼤提⾼程序运⾏效率。 在计算机中,有很多使⽤“池”这种技术的地⽅,除了内存池,还有连接池、线程池、对象池等。以服务器上的线程池为例,它的主要思想是:先启动若⼲数量的线程,让它们处于睡眠状态,当接收到客⼾端的请求时,唤醒池中某个睡眠的线程,让它来处理客⼾端的请求,当处理完这个请求,线程⼜进⼊睡眠状态。
2.内存池
内存池是指程序预先从操作系统申请⼀块⾜够⼤内存,此后,当程序中需要申请内存的时候,不是直 接向操作系统申请,⽽是直接从内存池中获取;同理,当程序释放内存的时候,并不真正将内存返回 给操作系统,⽽是返回内存池。当程序退出(或者特定时间)时,内存池才将之前申请的内存真正释放。
3.内存池主要解决的问题
内存池主要解决的当然是效率的问题,其次如果作为系统的内存分配器的⻆度,还需要解决⼀下内存 碎⽚的问题。那么什么是内存碎⽚呢? 再需要补充说明的是内存碎⽚分为外碎⽚和内碎⽚,上⾯我们讲的外碎⽚问题。外部碎⽚是⼀些空闲的连续内存区域太⼩,这些内存空间不连续,以⾄于合计的内存⾜够,但是不能满⾜⼀些的内存分配申请需求。内部碎⽚是由于⼀些对⻬的需求,导致分配出去的空间中⼀些内存⽆法被利⽤。内碎⽚问题,我们后⾯项⽬就会看到,那会再进⾏更准确的理解。
四、设计⼀个定长的内存池
作为程序员(C/C++)我们知道申请内存使⽤的是malloc,malloc其实就是⼀个通⽤的⼤众货,什么场景 下都可以⽤,但是什么场景下都可以⽤就意味着什么场景下都不会有很⾼的性能,下⾯我们就先来设 计⼀个定⻓内存池做个开胃菜,当然这个定⻓内存池在我们后⾯的⾼并发内存池中也是有价值的,所 以学习他⽬的有两层,先熟悉⼀下简单内存池是如何控制的,第⼆他会作为我们后⾯内存池的⼀个基础组件。
1.windows和Linux下如何直接向堆申请⻚为单位的⼤块内存:
// 直接去堆上按⻚申请空间
//VirtualAlloc
//brk和mmap
inline static void* SystemAlloc(size_t kpage)
{
比特就业课
#ifdef _WIN32
void* ptr = VirtualAlloc(0, kpage*(1<<12),MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE);
#else
// linux
下
brk mmap
等
#endif
if (ptr == nullptr)
throw std::bad_alloc();
return ptr;
}
2.定长的内存池实现
#pragma once
#include<iostream>
template<typename T>
class ObjectPool
{
public:
T* New()
{
T* obj = nullptr;
//优先把换回来的内存块,重复利用
if (_freeList)
{
void* next = *((void**)_freeList);
obj = (T*)_freeList;
_freeList = next;
}
else
{
//剩余内存不够一个对象大小时,则重新开大块空间
if (_remainBytes < sizeof(T))
{
_remainBytes = 128 * 1024;
_memory = (char*)SystemAlloc(_remainBytes >> 13);
if (_memory == nullptr)
{
throw std::bad_alloc();
}
}
obj = (T*)_memory;
size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);
_memory += objSize;
_remainBytes -= objSize;
}
new(obj)T;// 定位new,显示调用T的构造函数初始化
return obj;
}
void Delete(T* obj)
{
// 显示调用析构函数清理对象
obj->~T();
//头插
*(void**)obj = _freeList;
_freeList = obj;
}
private:
char* _memory = nullptr;//指向大块内存的指针
size_t _remainBytes = 0;//大块内存在切分过程中剩余的字节数
void* _freeList = nullptr;//还回来过程中链接的自由链表的头指针
};