高并发内存池(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 _WIN32
void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
// linux下brk mmap等
#endif
if (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里面去切,如果没有多余的,就去找系统申请大块内存
如何释放?不需要释放,不需要担心内存是否会泄漏,因为只要进程结束,它也能正常释放