1. 程式人生 > >伺服器公共庫開發-記憶體池管理模組

伺服器公共庫開發-記憶體池管理模組

/********************************************************************
    created:    2008/08/01
    filename:     mempool.h
    author:        Lichuang
                
    purpose:    模擬SGI STL記憶體池的實現, 可以配置是否支援多執行緒
********************************************************************
*/

#include 
"mempool.h"
#include 
<string.h>

CMemPool::CMemPool()
    : m_pStartFree(NULL)
    , m_pEndFree(NULL)
    , m_nHeapSize(
0)                      
{
    ::memset(m_szFreeList, 
0sizeof(m_szFreeList));
}

CMemPool::
~CMemPool()
{
}

// 從記憶體池中分配尺寸為n的記憶體void* CMemPool::Allocate(size_t nSize)
{
    
if (nSize > MAX_BLOCK_SIZE)
    {
        
return ::malloc(nSize);
    }
    
if (0>= nSize)
    {
        
return NULL;
    }

    Obj
** ppFreeList;
    Obj
*  pResult;

    THREAD_LOCK;

    
// 獲得尺寸n的HASH表地址    ppFreeList = m_szFreeList + GetFreeListIndex(nSize);
    pResult 
=*ppFreeList;
    
if (NULL == pResult)
    {
        
// 如果之前沒有分配, 或者已經分配完畢了, 就呼叫refill函式重新分配
        
// 需要注意的是, 傳入refill的引數是經過對齊處理的        pResult = (Obj*)Refill(RoundUp(nSize));
    }
    
else
    {
        
// 否則就更新該HASH表的LIST頭節點指向下一個LIST的節點, 當分配完畢時, 頭結點為NULL*ppFreeList = pResult->pFreeListLink;
    }

    THREAD_UNLOCK;

    
return pResult;
}

void* CMemPool::Reallocate(void* p, size_t nOldSize, size_t nNewSize)
{
    
void* pResult;
    size_t nCopySize;

    
// 如果超過記憶體池所能承受的最大尺寸, 呼叫系統API重新分配記憶體if (nOldSize > (size_t)MAX_BLOCK_SIZE && nNewSize > (size_t)MAX_BLOCK_SIZE)
    {
        
return ::realloc(p, nNewSize);
    }

    
// 如果新老記憶體尺寸在對齊之後相同, 那麼直接返回if (RoundUp(nOldSize) == RoundUp(nNewSize))
        
return p;

    
// 首先按照新的尺寸分配記憶體    pResult = Allocate(nNewSize);
    
if (NULL == pResult)
    {
        
return NULL;
    }
    
// copy舊記憶體的資料到新的記憶體區域    nCopySize = nNewSize > nOldSize ? nOldSize : nNewSize;
    ::memcpy(pResult, p, nCopySize);
    
// 釋放舊記憶體區域    Deallocate(p, nOldSize);

    
return pResult;
}

// 將尺寸為n的記憶體回收到記憶體池中void CMemPool::Deallocate(void* p, size_t nSize)
{
    Obj
* pObj = (Obj *)p;
    Obj 
**ppFreeList;

    
if (0>= nSize)
    {
        
return;
    }
    
// 如果要回收的記憶體大於MAX_BLOCK_SIZE, 直接呼叫free回收記憶體if (nSize > MAX_BLOCK_SIZE)
    {
        ::free(p);
        
return;
    }

    
// 將回收的記憶體作為連結串列的頭回收    ppFreeList = m_szFreeList + GetFreeListIndex(nSize);

    THREAD_LOCK;

    pObj
->pFreeListLink =*ppFreeList;
    
*ppFreeList = pObj;

    THREAD_UNLOCK;
}

int CMemPool::GetMemSize()
{
    
return m_nHeapSize;
}

size_t CMemPool::RoundUp(size_t nBytes)
{
    
return (nBytes + ALIGN -1&~(ALIGN -1);
}

int CMemPool::GetFreeListIndex(size_t nBytes)
{
    
return (nBytes + ALIGN -1/ ALIGN -1;
}


char* CMemPool::AllocChunk(size_t nSize, int& nObjs)
{
    
char* pResult;
    
// 總共所需的記憶體    size_t nTotalBytes = nSize * nObjs;
    
// 剩餘的記憶體    size_t nBytesLeft = m_pEndFree - m_pStartFree;

    
// 如果剩餘的記憶體可以滿足需求, 就直接返回之, 並且更新記憶體池的指標if (nBytesLeft >= nTotalBytes)
    {
        pResult 
= m_pStartFree;
        m_pStartFree 
+= nTotalBytes;
        
return pResult;
    }

    
// 如果剩餘的記憶體大於單位記憶體數量, 也就是說至少還可以分配一個單位記憶體
    
// 計算出最多可以分配多少塊單位記憶體, 儲存至nobjs, 返回記憶體的指標if (nBytesLeft >= nSize)
    {
        nObjs 
= (int)(nBytesLeft / nSize);
        nTotalBytes 
= nSize * nObjs;
        pResult 
= m_pStartFree;
        m_pStartFree 
+= nTotalBytes;
        
return pResult;
    }

    
// 如果還有剩餘的記憶體, 將它放到對應的HASH-LIST頭部if (0< nBytesLeft)
    {
        Obj
** ppFreeList = m_szFreeList + GetFreeListIndex(nBytesLeft);
        ((Obj
*)m_pStartFree)->pFreeListLink =*ppFreeList;
        
*ppFreeList = (Obj*)m_pStartFree;
    }

    
// 需要獲取的記憶體, 注意第一次分配都要兩倍於total_bytes的大小
    
// 同時要加上原有的heap_size / 4的對齊值    size_t nBytesToGet =2* nTotalBytes + RoundUp(m_nHeapSize >>4);
    m_pStartFree 
= (char*)::malloc(nBytesToGet);

    
// 獲取成功 重新呼叫chunk_alloc函式分配記憶體if (NULL != m_pStartFree)
    {
        m_nHeapSize 
+= nBytesToGet;
        m_pEndFree 
= m_pStartFree + nBytesToGet;
        
return AllocChunk(nSize, nObjs);
    }

    
// 下面是獲取不成功的處理.

    
// 從下一個HASH-LIST中尋找可用的記憶體int i = (int)GetFreeListIndex(nSize) +1;
    Obj 
**ppFreeList, *p;
    
for (; i < BLOCK_LIST_NUM; ++i)
    {
        ppFreeList 
= m_szFreeList + i;
        p 
=*ppFreeList;

        
if (NULL != p)
        {
            
*ppFreeList = p->pFreeListLink;
            m_pStartFree 
= (char*)p;
            m_pEndFree 
= m_pStartFree + (i +1* ALIGN;
            
return AllocChunk(nSize, nObjs);
        }
    }

    m_pEndFree 
= NULL;

    
return NULL;
}

// 重新分配尺寸為n的記憶體, 其中n是經過位元組對齊處理的數void* CMemPool::Refill(size_t n)
{
    
// 每個連結串列每次初始化時最多LIST_NODE_NUM個元素int nObjs = LIST_NODE_NUM;
    
char* pChunk = AllocChunk(n, nObjs);
    Obj
** ppFreeList;
    Obj
* pResult;
    Obj 
*pCurrentObj, *pNextObj;
    
int i;

    
// 如果只請求成功了一個元素, 直接返回之if (1== nObjs)
    {
        
return pChunk;
    }
    
// 獲得尺寸n的HASH表地址    ppFreeList = m_szFreeList + GetFreeListIndex(n);

    
// 獲得請求的記憶體地址    pResult = (Obj*)pChunk;
    
// 請求了一個單位記憶體, 減少一個計數--nObjs;
    
// 從下一個單位開始將剩餘的obj連線起來*ppFreeList = pNextObj = (Obj*)(pChunk + n);

    
// 將剩餘的obj連線起來for (i =1; ; ++i)
    {
        pCurrentObj 
= pNextObj;
        pNextObj 
= (Obj*)((char*)pNextObj + n);

        
// 分配完畢, 下一個節點為NULL, 退出迴圈if (nObjs == i)
        {
            pCurrentObj
->pFreeListLink = NULL;
            
break;
        }

        pCurrentObj
->pFreeListLink = pNextObj;
    }

    
return pResult;
}