C++内存池的实现和原理(二)
1、#include "Alloctor.h" 文件
#ifndef _ALLOCTOR_H_ #define _ALLOCTOR_H_ //重载函数声明 void* operator new(size_t size); void operator delete(void* p); void* operator new[](size_t size); void operator delete[](void* p); //给重载后的operator函数换一个函数名 void* mem_alloc(size_t size); void mem_free(void* p); #endif // !_ALLOCTOR_H_
2、#include "Alloctor.cpp" 文件
#include"Alloctor.h" #include<stdlib.h> #include"MemoryMgr.hpp" //重载new运算符,单个内存分配 void* operator new(size_t nSize) { return MemoryMgr::Instance().allocMem(nSize); } //重载delete运算符,单个内存释放 void operator delete(void* p) { MemoryMgr::Instance().freeMem(p); } //重载new运算符,多个内存分配 void* operator new[](size_t nSize) { return MemoryMgr::Instance().allocMem(nSize); } //重载delete运算符,多个内存释放 void operator delete[](void* p) { MemoryMgr::Instance().freeMem(p); } //给重载后的new和delete
3、#include "MemoryMgr.hpp"文件
#ifndef _MemoryMgr_hpp_ #define _MemoryMgr_hpp_ #include<stdlib.h> #include<mutex> #include<assert.h>//断言 //定义在Debug模式下输出调试信息 #ifdef _DEBUG #include<stdio.h> #define xPrintf(...) printf(__VA_ARGS__) #else #define xPrintf(...) #endif // _DEBUG //一个内存块的最大内存大小 #define MAX_MEMORY_SZIE 1024 //提前声明MemoryAlloc类 class MemoryAlloc; //内存块描述信息 class MemoryBlock { public: //所属内存池 MemoryAlloc* pAlloc; //下一块位置 MemoryBlock* pNext; //内存块编号 int nID; //引用次数 int nRef; //是否在内存池中 bool pbool; private: //预留 char c1; char c2; char c3; };//32位系统中指针大小位4个字节,64位为8个字节 //windows系统中得内存分配为8的整数倍个字节,所有这里一个内存块的大小为32个字节 //const int a = sizeof(MemoryBlock); //内存池 class MemoryAlloc { public: MemoryAlloc() { _pBuf = nullptr; _pHeader = nullptr; _nSize = 0; _nBlock = 0; xPrintf("MemoryAlloc\n"); } ~MemoryAlloc() { if (_pBuf) free(_pBuf); } void* allocMemory(size_t nSize) { std::lock_guard<std::mutex> lg(_mutex); //如果内存池一开始没有被分配地址 if (!_pBuf) { initMemory(); } MemoryBlock* pReturn = nullptr; //当该内存池的空间不足(已经到了尾指针的地方),需要申请额外内存 if (_pHeader == nullptr) { //给分配内存大小时,还要加上被分配内存的 描述信息的内存大小 pReturn = (MemoryBlock*)malloc(_nSize+sizeof(MemoryBlock)); pReturn->nID = -1; pReturn->nRef = 1; pReturn->pbool = false; pReturn->pAlloc = nullptr; pReturn->pNext = nullptr; } else { pReturn = _pHeader; _pHeader = _pHeader->pNext; assert(pReturn->nRef == 0); pReturn->nRef = 1; } //在Debug模式下输出调试信息 申请内存地址+内存块编号+内存块大小 xPrintf("allocMem: %llx, id=%d, size=%d\n", pReturn, pReturn->nID, nSize); //申请内存之后返回给用户的因该是申请的内存,所有首地址要往后偏移一个内存块大小 return ((char*)pReturn+sizeof(MemoryBlock)); } //释放内存 void freeMemory(void* pMem) { //首地址往前偏移,内存块和内存一起释放 MemoryBlock* pBlock = (MemoryBlock*)((char*)pMem - sizeof(MemoryBlock)); assert(1 == pBlock->nRef); //在内存池的部分释放之后,指针回到内存池头部 if (pBlock->pbool) { std::lock_guard<std::mutex> lg(_mutex); //只被引用次数减1,只有被引用次数为0的时候才需要释放内存 if (--pBlock->nRef != 0) { return; } //_pHeader是第一块空闲的内存,当pBlock释放出来之后,pBlock变成了第一块可用的内存 //_pHeader变成了第二块 //这里的额释放不是真正的把内存释放回给系统 pBlock->pNext = _pHeader; _pHeader = pBlock; } //不在内存池的部分直接释放 else { if (--pBlock->nRef != 0) { return; } free(pBlock); } } //初始化内存池 void initMemory() { xPrintf("initMemory:_nSize=%d,_nBlock=%d\n", _nSize, _nBlock); //断言 assert(_pBuf == nullptr); //内存池为空,不初始化直接返回 if (_pBuf) return; //计算内存池大小=内存块数量*(内存块大小+内存描述信息内存大小) size_t realSize = _nSize + sizeof(MemoryBlock); size_t bufSize = realSize * _nBlock; //向系统申请内存池大小 _pBuf = (char*)malloc(bufSize); //初始化内存块头指针 _pHeader = (MemoryBlock*)_pBuf; _pHeader->nID = 0; _pHeader->nRef = 0; _pHeader->pAlloc = this; _pHeader->pbool = true; _pHeader->pNext = nullptr; //遍历内存块并初始化 MemoryBlock* pTemp1 = _pHeader; for (size_t i = 1; i < _nBlock; i++) { MemoryBlock* pTemp2 = (MemoryBlock*)(_pBuf + i * realSize); pTemp2->nID = i; pTemp2->nRef = 0; pTemp2->pAlloc = this; pTemp2->pbool = true; pTemp2->pNext = nullptr; pTemp1->pNext = pTemp2; pTemp1 = pTemp2; } } protected: //内存池首地址 char* _pBuf; //内存块的头指针 MemoryBlock* _pHeader; //内存块的大小 size_t _nSize; //内存块的数量 size_t _nBlock; std::mutex _mutex; }; //提供模板,便于在声明类成员变量是初始化MemoryAlloc的成员数据 template<size_t nSize,size_t nBlock> class MemoryAlloctor:public MemoryAlloc { public: MemoryAlloctor() { //n指针大小 const size_t n = sizeof(void*); //保证内存对齐 if (nSize%n == 0) _nSize = nSize; else _nSize = (nSize / n)*n + n; _nBlock = nBlock; } }; //内存池管理工具 class MemoryMgr { private: MemoryMgr() { //初始化内存池映射数组_szAlloc,当申请一个内存时,可以直接找到内存大小合适的内存池 //init_szAlloc的功能类似映射, //如果申请内存的大小在[0,64]范围内,就在块大小为64的内存池去申请 //如果申请内存的大小在[65,128]范围内,就在块大小为128的内存池去申请 //..... init_szAlloc(0, 64, &_mem64); init_szAlloc(65, 128, &_mem128); init_szAlloc(129, 256, &_mem256); init_szAlloc(257, 512, &_mem512); init_szAlloc(513, 1024, &_mem1024); } ~MemoryMgr() { } public: //单例模式+静态对象 static MemoryMgr& Instance() { static MemoryMgr mgr; return mgr; } //申请内存 void* allocMem(size_t nSize) { if (nSize <= MAX_MEMORY_SZIE) { return _szAlloc[nSize]->allocMemory(nSize); } else { //没有足够大小的内存池,去额外申请内存 MemoryBlock* pReturn = (MemoryBlock*)malloc(nSize + sizeof(MemoryBlock)); pReturn->pbool = false; pReturn->nID = -1; pReturn->nRef = 1; pReturn->pAlloc = nullptr; pReturn->pNext = nullptr; //在Debug模式下输出调试信息 申请内存地址+内存块编号+内存块大小 xPrintf("allocMem: %llx, id=%d, size=%d\n", pReturn, pReturn->nID, nSize); //申请内存之后返回给用户的因该是申请的内存,所有首地址要往后偏移一个内存块大小 return ((char*)pReturn + sizeof(MemoryBlock)); } } //释放内存 void freeMem(void* pMem) { //地址往前偏移,//内存块和内存一起释放 MemoryBlock* pBlock = (MemoryBlock*)((char*)pMem - sizeof(MemoryBlock)); //在Debug模式下输出调试信息 释放内存地址+内存块编号 xPrintf("freeMem: %llx, id=%d\n", pBlock, pBlock->nID); //释放内存池 if (pBlock->pbool) { pBlock->pAlloc->freeMemory(pMem); } //释放额外内存 else { //只被引用一次就直接释放内存,若被多个引用暂时不释放(因为还被其它地方引用) if (--pBlock->nRef == 0) free(pBlock); } } //增加内存块的引用计数 void addRef(void* pMem) { MemoryBlock* pBlock = (MemoryBlock*)((char*)pMem - sizeof(MemoryBlock)); ++pBlock->nRef; } private: //初始化内存池映射数组 void init_szAlloc(int nBegin, int nEnd, MemoryAlloc* pMemA) { for (int i = nBegin; i <= nEnd; i++) { //内存池指针数组 _szAlloc[i] = pMemA; } } private: //内存池对外接口,不同大小的内存池 MemoryAlloctor<64, 100000> _mem64; MemoryAlloctor<128, 100000> _mem128; MemoryAlloctor<256, 100000> _mem256; MemoryAlloctor<512, 100000> _mem512; MemoryAlloctor<1024, 100000> _mem1024; //内存池指针数组 MemoryAlloc* _szAlloc[MAX_MEMORY_SZIE + 1]; }; #endif // !_MemoryMgr_hpp_
4、main.cpp文件
#include"Alloctor.h" #include<stdlib.h> #include<iostream> #include<thread> #include<mutex>//锁 #include"CELLTimestamp.hpp" using namespace std; //原子操作 原子 分子 mutex m; //线程数 const int tCount = 8; //内存申请次数 const int mCount = 100000; //每个线程申请次数 const int nCount = mCount / tCount; void workFun(int index) { char* data[nCount]; for (size_t i = 0; i < nCount; i++) { data[i] = new char[(rand() % 128) + 1]; } for (size_t i = 0; i < nCount; i++) { delete[] data[i]; } /* for (int n = 0; n < nCount; n++) { //自解锁 //lock_guard<mutex> lg(m); //临界区域-开始 //m.lock(); //m.unlock(); //临界区域-结束 }//线程安全 线程不安全 //原子操作 计算机处理命令时最小的操作单位 */ }//抢占式 int main() { thread t[tCount]; for (int n = 0; n < tCount; n++) { t[n] = thread(workFun, n); } CELLTimestamp tTime; for (int n = 0; n < tCount; n++) { t[n].join(); //t[n].detach(); } cout << tTime.getElapsedTimeInMilliSec() << endl; cout << "Hello,main thread." << endl; system("pause"); return 0; }
5、计时器文件
#ifndef _CELLTimestamp_hpp_ #define _CELLTimestamp_hpp_ //#include <windows.h> #include<chrono> using namespace std::chrono; class CELLTimestamp { public: CELLTimestamp() { update(); } ~CELLTimestamp() { } void update() { //设置一个持续的时间段 _begin = high_resolution_clock::now(); } double getElapsedSecond() { //毫秒转化成秒 return getElapsedTimeInMicroSec() * 0.000001; } double getElapsedTimeInMilliSec() { //微秒转化成毫秒 return this->getElapsedTimeInMicroSec() * 0.001; } /** * 获取微妙 */ long long getElapsedTimeInMicroSec() { //当前时间-进入时间,获取从开始计时到现在经历过的时间,以微妙为单位 return duration_cast<microseconds>(high_resolution_clock::now() - _begin).count(); } protected: //设置一个高精度时间点 time_point<high_resolution_clock> _begin; }; #endif // !_CELLTimestamp_hpp_
等风起的那一天,我已准备好一切