高并发内存池(1)-定长内存池
高并发内存池(1)-定长内存池
可以采用两种方式:
方式1:
template <size_t N>
方式2:
template <class T>
获取到T对象大小的内存池,更推荐使用方式二,因为可以动态灵活调整类型
需要的成员变量:
_memory:表示一大块内存,需要确定使用的变量类型
能否使用void*?
*表示的是指针,指针代表的是地址,它的指向需要有意义, *的前面表示的是类型,void * 没有意义,既不能解引用又不能进行加减
解决方案就是换成char*,因为一个char表示一个字节,也可以用int之类的,但是不会方便,比如要是取3字节啥的
怎么管理需要还回来的链表?
用自由链表
怎么样去连接?
不用结构体,用其内存块当节点,用头四个或者八个节点存储下一个位置的地址,这时候需要处理如果只剩最后一个会不会越界的情况,这时候我们需要引入新的成员变量remianBytes来知道剩余内存的大小,
自由链表需要进行头插,注意类型是void*,因为void *在32位下是4字节,64位是八字节,不用写if else条件判断语句去判断
整体的代码如下:
#pragma once#include <iostream>
#include <vector>
#include <time.h>using std::cout;
using std::endl;#ifdef _WIN32
#include <windows.h>
#else
// ...
#endif// 直接去堆上按页申请空间
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else// linux下brk mmap等
#endifif (ptr == nullptr)throw std::bad_alloc();return ptr;
}//定长内存池:每次获取到T对象大小的内存池
template <class T>
class ObjectPool
{public://对内存进行分配T* New(){//创建每次需要分配的内存块T* obj =nullptr;//优先使用自由链表里面的if (_freeList){//定义next指针void* next = *((void**)_freeList);obj = (T*)_freeList;_freeList = next;}//如果自由链表里面的不够了,再进行创建else{// 剩余内存不够一个对象大小时,则重新开大块空间if (remainBytes < sizeof(T)){//初始化remainBytes的大小remainBytes = 128 * 1024;//空间开辟大小,右移13相当于除以2的十三次方,2的10次方已经是1000多_memory = (char*)SystemAlloc(_remainBytes >> 13);//如果开创空间有异常就抛出异常if (_memory == nullptr){throw std::bad_alloc();}}//此时创建空间obj = (T*)_memory;//判断T对象的大小,void*在32位下是4字节,64位下是8字节size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);//切下空间_memory += objSize;//剩余空间去扣除相应的大小remainBytes -= objSize;}// 定位new,显示调用T的构造函数初始化,以防会调用如vector,string之类的new(obj)T();return obj;}//自由链表进行管理T* Delete(T*obj){// 显示调用析构函数清理对象obj->~T();// 头插,存前4个或者8个字节到下一节点*(void**)obj = _freeList;_freeList = obj;}private:char* _memory = nullptr;// 指向大块内存的指针size_t remainBytes = 0;// 大块内存在切分过程中剩余字节数void* _freeList = nullptr;// 还回来过程中链接的自由链表的头指针
};
整体的流程:先看freeList有没有空余的,先进行头删,然后再到_memory里面去切,如果没有多余的,就去找系统申请大块内存
如何释放?不需要释放,不需要担心内存是否会泄漏,因为只要进程结束,它也能正常释放