一、项目介绍
当前项⽬是实现⼀个⾼并发的内存池,他的原型是google的⼀个开源项⽬tcmalloc,tcmalloc全称 Thread-Caching Malloc,即线程缓存的malloc,实现了⾼效的多线程内存管理,⽤于替代系统的内 存分配相关的函数(malloc、free)。
这个项⽬会⽤到C/C++、数据结构(链表、哈希桶)、操作系统内存管理、单例模式、多线程、互斥锁 等等⽅⾯的知识。
1、池化技术
所谓池化技术,就是程序先向系统申请过量的资源,然后自己管理,当程序中需要申请内存时,不是直接向操作系统申请,而是直接从内存池中获取,释放内存时也不是将内存返回给操作系统,而是返回内存池中。
因为每次申请该资源都有较大的开销,这样提前申请好了,使用时就会非常快捷,能够大大提高程序运行效率。
在计算机中有很多使用这种池技术的地方,例如线程池、连接池等。
以服务器上的线程池为例,它的主要思想是:先启动若⼲数量的线程,让它们处于睡眠状态,当接收到 客⼾端的请求时,唤醒池中某个睡眠的线程,让它来处理客⼾端的请求,当处理完这个请求,线程⼜进⼊睡眠状态。
2、主要解决的问题
内存碎⽚分为外碎⽚和内碎⽚。外部碎⽚是⼀些空闲的连续内存区域太⼩,这些内存空间不连续,以⾄于合计的内存⾜够,但是不能满⾜⼀些的内存分配申请需求。内部碎⽚是由于⼀些对⻬的需求,导致分配出去的空间中⼀些内存⽆法被利⽤。
3、malloc
- C++中动态申请内存都是通过malloc去申请的,但实际上我们并不是直接去堆中获取内存的,而malloc就是一个内存池。
- malloc() 相当于向系统 “批发” 了一块较大的内存空间,然后“零售” 给程序使用,当全部使用完或者程序有大量内存需求时,再根据需求向操作系统申请内存。
⼀⽂了解,Linux内存管理,malloc、free 实现原理
二、定长内存池设计
1、开辟内存
- 使用malloc开辟一大块内存,让_memory指针指向这个大块内存
- _memory 设置为char* 类型,是为了方便切割时_memory向后移动多少字节数。
2、申请内存
- 将_memory强转为对应类型,然后赋值给对方,_memory指针向后移动对应字节数即可。
- 如果有存在已经切割好的小块内存,则优先使用小块内存。
3、释放内存
- 用类型链表的结构来进行存储。
- 用当前小块内存的头4字节存储下一个小块内存的地址,最后用_freeList指针指向第一个小块内存的地址(并不是将内存释放给操作系统)
- 所以开辟内存时,开辟的内存大小必须大于或等于一个指针类型的大小。
4、向堆申请页为单位的大块内存
- windows下:VirtualAlloc
- brk和mmapLinux下:brk和mmap
5、代码实现
#ifdef _WIN32 //如果32位则包含头文件 #include<windows.h> #else // #endif //定长内存池 //template<size_t N> //class ObjectPool //{}; // 直接去堆上按页申请空间 inline static void* SystemAlloc(size_t kpage) { #ifdef _WIN32 // 参数: 分配的区域的起始地址,区域的大小(字节),分配的类型,内存保护 void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // kpage << 13 以页为单位进行内存分配 #else // linux下brk mmap等 #endif if (ptr == nullptr) throw std::bad_alloc(); return ptr; } template<class T> class ObjectPool { public: T* New() { T* obj = nullptr; //优先利用还回来的内存 if (_freeList) { void* next = *((void**)_freeList); //转为void**类型,变为下一个结点的指针 obj = (T*)_freeList; _freeList = next; } else { //剩余内存不够一个大小时重新开大块空间 if (_remainBytes < sizeof(T)) { _remainBytes = 128 * 1024; //_memory = (char*)malloc(_remainBytes); _memory = (char*)SystemAlloc(_remainBytes >> 13); //_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,显示调用T的构造函数初始化 new(obj)T; return obj; } vo




1452

被折叠的 条评论
为什么被折叠?



