一个简单的C++内存管理与引用计数指针

来源:互联网 发布:仓库货物记账软件 编辑:程序博客网 时间:2024/05/01 20:38

最近项目需要一些模式相对固定的内存申请和销毁,手痒写了个C++内存管理器和引用计数的智能指针,加入了一些多线程保护机制(未测试)。代码见文章后半部分。

在main()里做了一个简单速度测试。在我的i5 4590+8G+win7机器上,使用定制内存管理的程序耗时0.35秒,使用new/delete的耗时16.9秒。这个内存管理代码还可以根据堆上内存的申请和销毁模式做进一步定制。


MemManagement.h

#ifndef __MEMORY_MANAGEMENT_H_INCLUDED__#define __MEMORY_MANAGEMENT_H_INCLUDED__#include <mutex>#include <atomic>#include <iostream>// "Recreationally" created by pkathlon, 2017/06/25// Feel free to use, modify, and restructure itnamespace Customized_MemoryManagement {template <typename T>class FixedAllocatedMem {public:FixedAllocatedMem() {m_pSegHeader = nullptr;}FixedAllocatedMem(unsigned int nBlocks, unsigned int nElementsPerBlock) {if (nBlocks == 0 || nElementsPerBlock == 0) {return;}MemSegInfo *pPrevSeg;for (int i = 0; i < nBlocks; ++i) {MemSegInfo *newInfo = new MemSegInfo;newInfo->_ptr = new T[nElementsPerBlock];newInfo->_sz = nElementsPerBlock;newInfo->_state = 0x01;newInfo->_pNext = nullptr;if (i == 0) {m_pSegHeader = newInfo;}else {pPrevSeg->_pNext = newInfo;}pPrevSeg = newInfo;}}~FixedAllocatedMem() {ReleaseMem();if (m_pSegHeader != nullptr) {throw "Not all memory is returned at destructor!";}}T* AllocateMem(unsigned int nElements) {std::lock_guard<std::mutex> lock(m_Mutex);// this NAIVE strategy (N required, N newed)// by PKATHLON can be easily improved// with pre-allocation or other techniquesif (m_pSegHeader == nullptr) {m_pSegHeader = new MemSegInfo;m_pSegHeader->_ptr = new T[nElements];m_pSegHeader->_sz = nElements;m_pSegHeader->_state = 0x03;m_pSegHeader->_pNext = nullptr;return m_pSegHeader->_ptr;}MemSegInfo *pPrevSeg = nullptr;for (MemSegInfo *pMSI = m_pSegHeader; pMSI; pPrevSeg = pMSI, pMSI = pMSI->_pNext) {if ((pMSI->_state & 0x02) || pMSI->_sz < nElements) {continue; // occupied or insufficient size}if (pMSI->_sz > nElements) { // splitMemSegInfo *newInfo = new MemSegInfo;newInfo->_ptr = pMSI->_ptr + nElements;newInfo->_sz = pMSI->_sz - nElements;newInfo->_state = 0x00;newInfo->_pNext = pMSI->_pNext;pMSI->_sz = nElements;pMSI->_pNext = newInfo;}pMSI->_state |= 0x02;return pMSI->_ptr;}// append a new segMemSegInfo *newInfo = new MemSegInfo;newInfo->_ptr = new T[nElements];newInfo->_sz = nElements;newInfo->_state = 0x03;newInfo->_pNext = nullptr;pPrevSeg->_pNext = newInfo;return newInfo->_ptr;}bool ReturnMem(T *memPtr) {std::lock_guard<std::mutex> lock(m_Mutex); // PKATHLON is definitely an RAII-enthusiast!MemSegInfo *pPrevSeg = nullptr;for (MemSegInfo *pMSI = m_pSegHeader; pMSI; pPrevSeg = pMSI, pMSI = pMSI->_pNext) {if (memPtr == pMSI->_ptr) {/*if ((pMSI->_state & 0x02) == 0) {// try to return non-occupied memory}*/pMSI->_state &= 0xffffffd;MemSegInfo *pMergedPtr = pMSI;if ((pPrevSeg != nullptr) && (pPrevSeg->_state & 0x02) == 0 && (pMSI->_state & 0x01) == 0) {// cur seg available to be merged into the prev segpPrevSeg->_sz += pMSI->_sz;pPrevSeg->_pNext = pMSI->_pNext;pMergedPtr = pPrevSeg;}if (pMSI->_pNext != nullptr && pMSI->_pNext->_state == 0) {// next seg available to be mergedpMergedPtr->_sz += pMSI->_pNext->_sz;MemSegInfo *pMergedNext = pMSI->_pNext;pMergedPtr->_pNext = pMSI->_pNext->_pNext;delete pMergedNext;}if (pMergedPtr != pMSI) {delete pMSI; // PKATHLON thinks there may be more involved (but more efficient)// way to deal with these little elements in a linked list}return true;}}return false; // incorrect memPtr}void ReleaseMem() {std::lock_guard<std::mutex> lock(m_Mutex);// only block heads can be actually "released" (returned to OS)// and delete[] pInTheMiddle can cause serious problemsif (m_pSegHeader == nullptr) {return;}while (m_pSegHeader->_state == 0x01) {if (m_pSegHeader->_pNext == nullptr|| (m_pSegHeader->_pNext->_state & 0x01)) {delete[] m_pSegHeader->_ptr;MemSegInfo *pn = m_pSegHeader->_pNext;delete m_pSegHeader;m_pSegHeader = pn;if (pn == nullptr) {return;}}else {break;}}MemSegInfo *pPrevSeg = m_pSegHeader;for (MemSegInfo *pCurSeg = m_pSegHeader->_pNext; pCurSeg;pPrevSeg = pCurSeg, pCurSeg = pCurSeg->_pNext) {if (pCurSeg->_state != 0x01) {continue;}if (pCurSeg->_pNext == nullptr|| (pCurSeg->_pNext->_state & 0x01)) {delete[] pCurSeg->_ptr;MemSegInfo *pn = pCurSeg->_pNext;delete pCurSeg;pPrevSeg->_pNext = pn;pCurSeg = pPrevSeg;}}}void _displayChainedMemorySegments() const {// a tool for testingstd::cout << "\nChained Memory:\n";if (m_pSegHeader == nullptr) {std::cout << "No available memory block!\n";}int cnt = 0;for (MemSegInfo *pSeg = m_pSegHeader; pSeg != nullptr; pSeg = pSeg->_pNext, ++cnt) {std::cout << "Seg. No." << cnt << ", size = " << pSeg->_sz <<", occupied=" << (bool)(pSeg->_state & 0x02)<< ", blockHead=" << (bool)(pSeg->_state & 0x01) << std::endl;}}protected:struct MemSegInfo {T *_ptr;unsigned int _sz;int _state; // bit 0 - if it's a block head, bit 1 - if it's occupiedMemSegInfo *_pNext;}; // memory blocks are recorded in linked chainsMemSegInfo *m_pSegHeader;std::mutex m_Mutex;};template <typename T>class MemPointer {public:MemPointer() {m_pRefInfo = nullptr;}MemPointer(T *pMemWeWillNOTRelease) {m_pRefInfo = g_RefS_MemMgt.AllocateMem(1);m_pRefInfo->_bResponsible = false;m_pRefInfo->_pData = pMemWeWillNOTRelease;}MemPointer(MemPointer &oriMP) {if (++(oriMP.m_pRefInfo->_nRefCnt) == 1) {m_pRefInfo = nullptr;}else {m_pRefInfo = oriMP.m_pRefInfo;}}MemPointer(unsigned int nElementSize) {m_pRefInfo = g_RefS_MemMgt.AllocateMem(1);m_pRefInfo->_bResponsible = true;m_pRefInfo->_size = nElementSize;m_pRefInfo->_nRefCnt = 1;m_pRefInfo->_pData = g_Data_MemMgt.AllocateMem(nElementSize);}MemPointer& operator= (MemPointer &oriMP) {refStruct *dstRefStruct;if (++(oriMP.m_pRefInfo->_nRefCnt) == 1) {dstRefStruct = nullptr;}else {dstRefStruct = oriMP.m_pRefInfo;}if (this->m_pRefInfo && this->m_pRefInfo->_bResponsible) {if (--(this->m_pRefInfo->_nRefCnt) == 0) {g_Data_MemMgt.ReturnMem(this->m_pRefInfo->_pData);g_RefS_MemMgt.ReturnMem(this->m_pRefInfo);}}this->m_pRefInfo = dstRefStruct;return (*this);}MemPointer& operator= (T *pMemWeWillNOTRelease) {if (this->m_pRefInfo && this->m_pRefInfo->_bResponsible) {if (--(this->m_pRefInfo->_nRefCnt) == 0) {g_Data_MemMgt.ReturnMem(this->m_pRefInfo->_pData);g_RefS_MemMgt.ReturnMem(this->m_pRefInfo);}}m_pRefInfo = g_RefS_MemMgt.AllocateMem(1);m_pRefInfo->_bResponsible = false;m_pRefInfo->_pData = pMemWeWillNOTRelease;return (*this);}~MemPointer() {if (m_pRefInfo) {if (m_pRefInfo->_bResponsible) {if (--(m_pRefInfo->_nRefCnt) == 0) {g_Data_MemMgt.ReturnMem(m_pRefInfo->_pData);g_RefS_MemMgt.ReturnMem(m_pRefInfo);}}else {g_RefS_MemMgt.ReturnMem(m_pRefInfo);} }}T& operator[] (int index) { // now access elements in an array like you learned it from the firs time!if (m_pRefInfo == nullptr) {throw "No memory is pointed!";}else if (m_pRefInfo->_bResponsible && (index < 0 || index >= (int)m_pRefInfo->_size)) {throw "Array index out of bound!"; // PKATHLON: should I care?}return (m_pRefInfo->_pData)[index];}unsigned int Size() const {if (m_pRefInfo == nullptr) {throw "No memory is pointed!";}else if (!m_pRefInfo->_bResponsible) {return -1;}else {return m_pRefInfo->_size;}}protected:struct refStruct {bool _bResponsible;unsigned int _size; // number of elements <T>std::atomic_int _nRefCnt; // atomic types is the single best feature of c++11 according to PKATHLONT *_pData;};refStruct *m_pRefInfo;static FixedAllocatedMem<T> g_Data_MemMgt;static FixedAllocatedMem<refStruct> g_RefS_MemMgt;};template <typename T> FixedAllocatedMem<T> MemPointer<T>::g_Data_MemMgt;template <typename T> FixedAllocatedMem<typename MemPointer<T>::refStruct> MemPointer<T>::g_RefS_MemMgt;}#endif




main.cpp

#include <chrono>#include "MemManagement.h"int main(){std::chrono::system_clock::time_point t0 = std::chrono::high_resolution_clock::now();#if 0using namespace Customized_MemoryManagement;for (int i = 0; i < 1000000; ++i){MemPointer<double> p0(10000);MemPointer<double> p1 = p0;p1[0] = 3.0;p1[1] = 1.1;double asd[10000];p0 = asd;p0[0] = -3.0;p0[1] = -1.1; // don't need to care about memory deallocation}#elsefor (int i = 0; i < 1000000; ++i){double *p0 = new double[10000];double *p1 = p0;p1[0] = 3.0;p1[2] = 1.1;double asd[10000];p0 = asd;p0[0] = -3.0;p0[1] = -1.1;delete[] p1; // "delete[] p0;" will cause memory leak and crash here!}#endifstd::chrono::system_clock::time_point t1 = std::chrono::high_resolution_clock::now();double timediff = std::chrono::duration_cast<std::chrono::nanoseconds>(t1 - t0).count() / (1e6);std::cout << "Elapsed time = " << timediff << " milliseconds...\n";getchar();return 0;}


原创粉丝点击