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

最近项目需要一些模式相对固定的内存申请和销毁,手痒写了个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 it
namespace 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 techniques
if (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) { // split
MemSegInfo *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 seg
MemSegInfo *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 seg
pPrevSeg->_sz += pMSI->_sz;
pPrevSeg->_pNext = pMSI->_pNext;
pMergedPtr = pPrevSeg;
}
if (pMSI->_pNext != nullptr && pMSI->_pNext->_state == 0) {
// next seg available to be merged
pMergedPtr->_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 problems
if (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 testing
std::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 occupied
MemSegInfo *_pNext;
}; // memory blocks are recorded in linked chains
MemSegInfo *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 PKATHLON
T *_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 0
using 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
}
#else
for (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!
}
#endif
std::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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值