第四章(小型对象分配技术:smart-object allocation)

来源:互联网 发布:淘宝美工店铺装修教程 编辑:程序博客网 时间:2024/06/05 21:12

分配原理

block:最小单位,外部指定,一般每个block从4~64byte不等。我们称为blocksize

chunk:包含多个block。个数我们称为blocknum。每个chunk大小为blocknum*blocksize。一般为固定,大小我们称为page

fixedAllocator:包含多个chunk。里面的chunk的block大小都是固定的。即每个fixedAllocator包含的是一种类型的chunk.

smartObjectAlllocator:包含多个fixedAllocator。固定个数,根据外部初始化的时候指定。

smartObject:外部使用的类。

举个例子就明白了。如smartObject初始化一个page为4096byte。最小的block为4,最大的为64,每次增长4个byte。这些值都是初始化可以设定改变的

那么smartObjectAllocator包含的fixedAllocator依次为:4*1, 4*2, 4*3....4*16大小的blocksize的chunk。有16个不同类型的fiexdAllocator

第一个fixAllocator中包含的Chunk的类型是:每个block为4byte。包含的block的个数定义为unsigned char类型,不超过255,包含255个block

最后一个fixAllocator中包含的chunk的信息室:每个block的大小是64bte,个数是4096/64=64。即包含64个连续的block

如果用户需要分配16byte类型的内存,则从里面找到最合适的Chunk,然后分配给它。fixedAllocator默认是不包含任何Chunk的,如果需要使用,则new一个出来。

代码实现

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Chunk.h

/** @file    Chunk.h
 *  @note    HangZhou Hikvision Digital Technology Co., Ltd. All Right Reserved.
 *  @brief   一个内存分配器中,单个Chunk的大小是固定的,为blocksize * numblocks
             然后每个Chunk的分配方式是不同的。如单个blocksize大小为16,则numblocks的大小就为pagesize/16
 *  @author  hjing
 *  @date    2015-06-19
 */
#pragma once

class CChunk
{
    friend class CFixedAllocator;
private:
    // 初始化
    bool Init(std::size_t blockSize, unsigned char numblocks);

    // 释放所有
    void Release();

    // 分配内存
    void* Allocate(std::size_t blockSize);

    // 释放内存
    void Deallocate(void* p, std::size_t blockSize);

    // 数据重置
    void Reset(std::size_t blockSize, unsigned char blocks);

    // 是否已经满了
    inline bool IsFilled() const {return blocksAvailable_ == 0;}

    /// Returns true if block at address P is inside this Chunk.
    // chunklength = bolcksize * numblocks
    inline bool HasBlock( void * p, std::size_t chunkLength ) const
    {
        unsigned char * pc = static_cast< unsigned char * >( p );
        return ( pData_ <= pc ) && ( pc < pData_ + chunkLength );
    }
   
    // 是否全部是空着的了,与IsFilled相反
    inline bool HasAvailable( unsigned char numBlocks ) const
    {
        return ( blocksAvailable_ == numBlocks );
    }

    unsigned char* pData_; // 原始内存,连续的
    unsigned char firstAvailableBlock_; // 第一个可用的block的序号,从0开始
    unsigned char blocksAvailable_; // 可用的block个数
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Chunk.cpp

#include "StdAfx.h"
#include "Chunk.h"


/** @fn    void CChunk::Init(std::size_t bolckSize, unsigned char blocks)
*  @brief  Init
*  @param  (IN) std::size_t bolckSize
*  @param  (IN) unsigned char blocks
*  @param  (OUT)
*  @return void
*/
bool CChunk::Init(std::size_t blockSize, unsigned char numblocks)
{
    assert(blockSize > 0);
    assert(numblocks > 0);

    // 此处为什么可用直接用(),而不是new[]数组呢?是因为直接调用了operator new,分配的内存大小是对的
    // 如果直接用pData_ = new char(10)和pData_ = new char[10];是有区别的,第一句值分配1个字节的内存
    pData_ = static_cast<unsigned char*>(::operator new (numblocks * blockSize)); // 此处使用C++的new
    Reset(blockSize, numblocks);

    return true;
}

/** @fn    void CChunk::Release()
*  @brief  Release
*  @param  (OUT)
*  @return void
*/
void CChunk::Release()
{
    ::operator delete (pData_);
    blocksAvailable_ = 0;
    firstAvailableBlock_ = 0;
}

/** @fn    void* CChunk::Allocate(std::size_t blockSize)
*  @brief  Allocate
*  @param  (IN) std::size_t blockSize
*  @param  (OUT)
*  @return void*
*/
void* CChunk::Allocate(std::size_t blockSize)
{
    if (!blocksAvailable_)
    {
        return NULL;
    }

    unsigned char* pResult = pData_ + (firstAvailableBlock_ * blockSize); // 首地址
    firstAvailableBlock_ = (*pResult);
    --blocksAvailable_;

    return pResult;
}

/** @fn    void CChunk::Deallocate(void* p, std::size_t blockSize)
*  @brief  Deallocate
*  @param  (IN) void * p
*  @param  (IN) std::size_t blockSize
*  @param  (OUT)
*  @return void
*/
void CChunk::Deallocate(void* p, std::size_t blockSize)
{
    assert(p >= pData_); // 必须来自于pdata_的内存

    unsigned char* toRelease = static_cast<unsigned char*>(p);
    assert((toRelease - pData_) % blockSize == 0); // 必须释放block的首地址

    unsigned char index = static_cast<unsigned char>((toRelease - pData_) / blockSize); // 需要释放的block号


    *toRelease = firstAvailableBlock_; // 这条语句很关键!!!如0,1,2分配了,firstAvailableBlock_变为了3,释放0,这个时候再把0的首地址值设置为3
                                       // 下面一句first设置为0,下次分配分配刚刚释放的0的,并且!把first会设置为3,即释放后,可用的firs会变为3,
                                       // 而不是0下面的1,即内存不是按照顺序释放的也是可以的。充分的利用了空余的空间的首地址
    firstAvailableBlock_ = index;
    //assert(firstAvailableBlock_ == (toRelease == (toRelease - pData_) / blockSize));
     ++blocksAvailable_;
}

/** @fn    void CChunk::Reset(std::size_t blockSize, size_t blocks)
*  @brief  Reset
*  @param  (IN) std::size_t blockSize
*  @param  (IN) size_t blocks
*  @param  (OUT)
*  @return void
*/
void CChunk::Reset(std::size_t blockSize, unsigned char numblocks)
{
    firstAvailableBlock_ = 0;
    blocksAvailable_ = numblocks;

    unsigned char* p = pData_;
    for (unsigned char i = 0; i < numblocks; p += blockSize)
    {
        *p = ++i; // 每块未使用的block块首地址的内容都是指向下一个block序号
    }  
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

FixedAllocator.h

/** @file    FixedAllocator.h
 *  @note    HangZhou Hikvision Digital Technology Co., Ltd. All Right Reserved.
 *  @brief   每个CFixAllocator包含有多个Chunk,每个Chunk的含义是一样的,只是编号不同
             每个Chunk最大能够支持255个block,每个CFixAllocator的空间称为一页
             每页的大小是固定的。如521Byte,1024Byte等
             每个Chunk中的Block大小也是固定的。如该FixAllocator中的Block大小为16,而页面大小是512则每个Chunk的BlockNum为512/16
 *  @author  hjing
 *  @date    2015-06-19
 */
#pragma once
#include "Chunk.h"
#include <vector>
using namespace std;
class CFixedAllocator
{
    typedef vector<CChunk>::iterator CItor;
    typedef vector<CChunk>::const_iterator ConstItor;
public:
    CFixedAllocator();
    ~CFixedAllocator();
    void Initialize( std::size_t blockSize, std::size_t pageSize );

    // 分配一个blocksize出去
    void *Allocate(void);
    bool Deallocate(void* p, CChunk* phint);

    bool TrimChunkList( void );

    inline std::size_t BlockSize() const { return blockSize_; }

    // 释放空的chunk
    bool TrimEmptyChunk();

    const CChunk * HasBlock( void * p ) const
    {

        const std::size_t chunkLength = numBlocks_ * blockSize_;
        for ( ConstItor it( chunks_.begin() ); it != chunks_.end(); ++it )
        {
            const CChunk & chunk = *it;
            if ( chunk.HasBlock( p, chunkLength ) )
                return &chunk;
        }
        return NULL;
    }
    inline CChunk * HasBlock( void * p )
    {
        return const_cast< CChunk * >(
            const_cast< const CFixedAllocator * >( this )->HasBlock( p ) );
    }
private:   

    // 新建一个chunk
    bool MakeNewChunk();

    // 通过指针查找属于哪个chunk
    CChunk * VicinityFind( void * p ) const;

    // 真正的释放p
    void DoDeallocate(void * p);

    std::size_t blockSize_; //  每个block的大小,每个block不建议超过64byte
    unsigned char numBlocks_;// 每个chunk的block个数,每个chunk最多只有255个block

    vector<CChunk> chunks_;
    CChunk* allocChunk_; // 最近一次分配所使用的chunk,提高分配效率
    CChunk* deallocChunk_; // 指向归还所用的最后那个chunk,提高释放效率。释放block的chunk

    // 指向一个全部为空的chunk,即该chunk中所有的block都可用。 Pointer to the only empty Chunk if there is one, else NULL.
    // 另外需要保证整个chunks_中最多只有一个Chunk是完全空白可用的(也可能没有完全空白的chunk),不要浪费空间
    CChunk* emptyChunk_;

    static unsigned char MinObjectsPerChunk_; // chunk最小个数
    static unsigned char MaxObjectsPerChunk_; // chunk最大个数
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

FixedAllocator.cpp

#include "StdAfx.h"
#include "FixedAllocator.h"

unsigned char CFixedAllocator::MinObjectsPerChunk_ = 8;
unsigned char CFixedAllocator::MaxObjectsPerChunk_ = UCHAR_MAX;

/** @fn     CFixedAllocator::CFixedAllocator()
*  @brief  CFixedAllocator
*  @param  (OUT)
*  @return
*/
CFixedAllocator::CFixedAllocator()
: blockSize_(0),
  numBlocks_(0),
  chunks_(0),
  allocChunk_(NULL),
  deallocChunk_(NULL),
  emptyChunk_(NULL)
{

}


/** @fn     CFixedAllocator::~CFixedAllocator()
*  @brief  ~CFixedAllocator
*  @param  (OUT)
*  @return
*/
CFixedAllocator::~CFixedAllocator()
{
    for ( CItor it = chunks_.begin(); it != chunks_.end(); ++it)
    {
        it->Release();
    }
}


/** @fn    void CFixedAllocator::Initialize(std::size_t blockSize, std::size_t pageSize)
*  @brief  Initialize
*  @param  (IN) std::size_t blockSize
*  @param  (IN) std::size_t pageSize
*  @param  (OUT)
*  @return void
*/
void CFixedAllocator::Initialize(std::size_t blockSize, std::size_t pageSize)
{
    blockSize_ = blockSize;

    std::size_t numBlocks = pageSize / blockSize;

    if (numBlocks > MaxObjectsPerChunk_)
    {
        numBlocks = MaxObjectsPerChunk_;
    }
    else if (numBlocks < MinObjectsPerChunk_)
    {
        numBlocks = MinObjectsPerChunk_;
    }

    numBlocks_ = static_cast<unsigned char>(numBlocks);

}

/** @fn    void * CFixedAllocator::Alloc(void)
*  @brief  Alloc
*  @param  (IN) void
*  @param  (OUT)
*  @return void *
*/
void* CFixedAllocator::Allocate(void)
{
    if (allocChunk_ == 0 || allocChunk_->IsFilled())
    {
        //
        if (NULL != emptyChunk_)
        {
            allocChunk_ = emptyChunk_;
            emptyChunk_ = NULL;
        }
        // allocchunk不可用,线性查找或则新建一个chunk
        CItor itfind = chunks_.begin();
        for (;;++itfind)
        {
            if (itfind == chunks_.end())
            {
                if (!MakeNewChunk())
                {
                    return NULL;
                }
                itfind = chunks_.end() - 1;
                assert(!itfind->IsFilled());
                allocChunk_ = &*itfind;
                break;
            }
            if (!itfind->IsFilled())
            {
                allocChunk_ = &*itfind;
                break;
            }
        }
    }
    else if (allocChunk_ == emptyChunk_)
    {
        emptyChunk_ = NULL;
    }
    assert(allocChunk_ != 0);
    assert(allocChunk_->blocksAvailable_ > 0);
    return allocChunk_->Allocate(blockSize_);
}

/** @fn    void* CFixedAllocator::Deallocate(void* p)
*  @brief  DealAlloc
*  @param  (IN) void * p
*  @param  (OUT)
*  @return void*
*/
bool CFixedAllocator::Deallocate(void* p, CChunk* phint)
{
    CChunk* fonudChunk = (NULL == phint) ? VicinityFind(p) : phint;

    if ( NULL == fonudChunk)
    {
        // 不合法的p
        return false;
    }
    deallocChunk_ = fonudChunk;
    DoDeallocate(p);
   
    return true;   
}

/** @fn    bool CFixedAllocator::MakeNewChunk()
*  @brief  MakeNewChunk
*  @param  (OUT)
*  @return bool
*/
bool CFixedAllocator::MakeNewChunk()
{
    bool allocated = false;
    try
    {
        std::streamsize size = chunks_.size();
        if (chunks_.capacity() == size)
        {
            if (0 == size)
            {
                size = 4;
            }
            chunks_.reserve(size * 2); // 主动控制chunks的大小,不用vector的能力
        }
        CChunk newChunk;
        allocated = newChunk.Init(blockSize_, numBlocks_);
        if (allocated)
        {
            chunks_.push_back(newChunk);
        }
    }
    catch (...)
    {

    }
    return allocated;
}

/** @fn    CChunk * CFixedAllocator::VicinityFind(void * p) const
*  @brief  通过指针查找属于哪个chunk
*  @param  (IN) void * p
*  @param  (OUT)
*  @return CChunk *
*/
CChunk * CFixedAllocator::VicinityFind(void * p) const
{
    if (chunks_.empty())
    {
        return NULL;
    }
    assert(deallocChunk_ != NULL);

    const std::size_t chunkLength = numBlocks_ * blockSize_;

    // 向前和向后找
    CChunk* lo = deallocChunk_;
    CChunk* hi = deallocChunk_ + 1;
    const CChunk* loBound = &chunks_.front();
    const CChunk* hiBound = &chunks_.back() + 1; // 无效值,chunks.end();

    if (hi == hiBound)
    {
        hi = NULL;
    }

    for (;;)
    {
        // 向begin方向找
        if (lo)
        {
            if ( lo->HasBlock( p, chunkLength ) )
            {
                return lo;
            }
            if ( lo == loBound )
            {
                lo = NULL;
                if ( NULL == hi )
                {
                    break;
                }
            }
            else
            {
                --lo;
            }
        }

        // 向end方向找
        if (hi)
        {
            if ( hi->HasBlock( p, chunkLength ) )
            {
                return hi;
            }
            if ( ++hi == hiBound )
            {
                hi = NULL;
                if ( NULL == lo )
                {
                    break;
                }
            }
        }
    }

    return NULL;
}

/** @fn    void CFixedAllocator::DoDeallocate(void * p)
*  @brief  真正的释放p
*  @param  (IN) void * p
*  @param  (OUT)
*  @return void
*/
void CFixedAllocator::DoDeallocate(void * p)
{
    // Show that deallocChunk_ really owns the block at address p.
    assert( deallocChunk_->HasBlock( p, numBlocks_ * blockSize_ ) );
    // Either of the next two assertions may fail if somebody tries to
    // delete the same block twice.
    assert( emptyChunk_ != deallocChunk_ );
    assert( !deallocChunk_->HasAvailable( numBlocks_ ) );
    // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
    assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );

    // call into the chunk, will adjust the inner list but won't release memory
    // 释放p所指的那块block
    deallocChunk_->Deallocate(p, blockSize_);

    // 如果全部被释放了,即p所指向的block全部可用了,需要清空一下内存,保证它不总是占用着真实的内存
    if ( deallocChunk_->HasAvailable( numBlocks_ ) )
    {
        assert( emptyChunk_ != deallocChunk_ ); // emptyChunk肯定是全部为空的,而deallocChunk_是刚刚才释放为空,前面肯定是被占用着的
        // deallocChunk_ is empty, but a Chunk is only released if there are 2
        // empty chunks.  Since emptyChunk_ may only point to a previously
        // cleared Chunk, if it points to something else besides deallocChunk_,
        // then FixedAllocator currently has 2 empty Chunks.
        // 避免边界条件发生,allocchunk_和dealalloc_都指向最后一个chunk,而该chunk也所剩无几
        if ( NULL != emptyChunk_ )
        {
            // If last Chunk is empty, just change what deallocChunk_
            // points to, and release the last.  Otherwise, swap an empty
            // Chunk with the last, and then release it.
            CChunk * lastChunk = &chunks_.back();
            if ( lastChunk == deallocChunk_ )
            {
                deallocChunk_ = emptyChunk_;
            }
            else if ( lastChunk != emptyChunk_ )
            {   
                std::swap( *emptyChunk_, *lastChunk );
            }
            assert( lastChunk->HasAvailable( numBlocks_ ) );
            lastChunk->Release(); // 如果DoDeallocate后,出现了一个完全空白的blocks,直接将此blocks内存释放出去,不要让它一直占用着
            chunks_.pop_back();   // 从chunk弹出
            if ( ( allocChunk_ == lastChunk ) || allocChunk_->IsFilled() )
                allocChunk_ = deallocChunk_; // 改变allocChunk的位置,如果allocChunk不再使用当allocChunk
        }
        emptyChunk_ = deallocChunk_;
    }

    // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
    assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) ); 
}


// FixedAllocator::TrimChunkList ----------------------------------------------

bool CFixedAllocator::TrimChunkList( void )
{
    if ( chunks_.empty() )
    {
        assert( NULL == allocChunk_ );
        assert( NULL == deallocChunk_ );
    }

    if ( chunks_.size() == chunks_.capacity() )
        return false;
    // Use the "make-a-temp-and-swap" trick to remove excess capacity.
    // 这种写法的原理如下,原作者写的一句话把很多东西省略了
    // vector<CChunk> vsTmp(chunks_); vsTmp的值就是chunks_,比如chunks_的大小为100
    // 此时vsTmp的capacity值就是100,但是chunks_的capacity肯定比100大,估计在120以上
    // swap一下,chunks_的capacity变回了100。vsTmpl临时变量,退出函数,被销毁,不用理睬
    (vector<CChunk>( chunks_ )).swap( chunks_ );

    return true;
}

// FixedAllocator::TrimEmptyChunk ---------------------------------------------

bool CFixedAllocator::TrimEmptyChunk( void )
{
    // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
    assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
    if ( NULL == emptyChunk_ ) return false;

    // If emptyChunk_ points to valid Chunk, then chunk list is not empty.
    assert( !chunks_.empty() );
    // And there should be exactly 1 empty Chunk.
    //assert( 1 == CountEmptyChunks() );

    CChunk * lastChunk = &chunks_.back();
    if ( lastChunk != emptyChunk_ )
        std::swap( *emptyChunk_, *lastChunk );
    assert( lastChunk->HasAvailable( numBlocks_ ) );
    lastChunk->Release();
    chunks_.pop_back();

    if ( chunks_.empty() )
    {
        allocChunk_ = NULL;
        deallocChunk_ = NULL;
    }
    else
    {
        if ( deallocChunk_ == emptyChunk_ )
        {
            deallocChunk_ = &chunks_.front();
            assert( deallocChunk_->blocksAvailable_ < numBlocks_ );
        }
        if ( allocChunk_ == emptyChunk_ )
        {
            allocChunk_ = &chunks_.back();
            assert( allocChunk_->blocksAvailable_ < numBlocks_ );
        }
    }

    emptyChunk_ = NULL;
    //assert( 0 == CountEmptyChunks() );

    return true;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

SmallObjAllocator.h

#pragma once
#include "FixedAllocator.h"
class CSmallObjAllocator
{
protected:
    CSmallObjAllocator(std::size_t pageSize, std::size_t maxObjectSize, std::size_t blockSize);
    ~CSmallObjAllocator();

public:
    void* Allocate(std::size_t numBytes);
    void Deallocate(void* p, std::size_t size);
    void Deallocate(void* p);

private:
    // 禁止拷贝
    CSmallObjAllocator();
    CSmallObjAllocator(const CSmallObjAllocator&);
    CSmallObjAllocator & operator = ( const CSmallObjAllocator & );

    // 字节增长,如4字节一个排列,则是4,8,12等不同的blocks
    std::size_t GetOffset(std::size_t numBytes, std::size_t objectAddSize)
    {
        return (numBytes + objectAddSize - 1) / objectAddSize;
    }

    inline std::size_t GetMaxObjectSize() const
    {
        return maxSmallObjectSize_;
    }

     inline std::size_t GetAlignment() const { return objectAlignSize_; }

    bool TrimExcessMemory( void );

    bool TrimChunkList( void );
private:
    CFixedAllocator* pool_; // 数组

    // 最大支持的blocks大小
    std::size_t maxSmallObjectSize_;

    /// Size of alignment boundaries.
    const std::size_t objectAlignSize_;
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

SmallObjAllocator.cpp

#include "StdAfx.h"
#include "SmallObjAllocator.h"
// c++默认分配器
// DefaultAllocator -----------------------------------------------------------
/** @ingroup SmallObjectGroupInternal
 Calls the default allocator when SmallObjAllocator decides not to handle a
 request.  SmallObjAllocator calls this if the number of bytes is bigger than
 the size which can be handled by any FixedAllocator.
 @param numBytes number of bytes
 @param doThrow True if this function should throw an exception, or false if it
  should indicate failure by returning a NULL pointer.
*/
void * DefaultAllocator( std::size_t numBytes)
{
    return operator new(numBytes);
}

void DefaultDeallocator( void * p )
{
    ::operator delete( p );
}

CSmallObjAllocator::CSmallObjAllocator(std::size_t pageSize, std::size_t maxObjectSize, std::size_t blockSize)
: pool_(NULL),
  maxSmallObjectSize_(maxObjectSize),
  objectAlignSize_(blockSize)
{
    const std::size_t allocCount =  GetOffset(maxObjectSize, blockSize); // 可用划分出多少个FixAllocator,每个FixAllocator是个
    pool_ = new CFixedAllocator[allocCount];

    for (std::size_t i = 0; i < allocCount; ++i)
    {
        pool_[i].Initialize((i + 1) * blockSize, pageSize);
    }
}

/** @fn    void* CSmallObjAllocator::Allocate(std::size_t numBytes)
*  @brief  Allocate
*  @param  (IN) std::size_t numBytes
*  @param  (OUT)
*  @return void*
*/
void* CSmallObjAllocator::Allocate(std::size_t numBytes)
{
    if ( numBytes > GetMaxObjectSize() )
        return DefaultAllocator( numBytes);

    assert( NULL != pool_ );
    if ( 0 == numBytes ) numBytes = 1;
    const std::size_t index = GetOffset( numBytes, GetAlignment() ) - 1;
    const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
    (void) allocCount;
    assert( index < allocCount );

    CFixedAllocator & allocator = pool_[ index ];// 找到numBytes最适合的CFixedAllocator
    assert( allocator.BlockSize() >= numBytes );
    assert( allocator.BlockSize() < numBytes + GetAlignment() );
    void * place = allocator.Allocate();

    if ( ( NULL == place ) && TrimExcessMemory() )
        place = allocator.Allocate();
    return place;
}

/** @fn    void CSmallObjAllocator::Deallocate(void* p, std::size_t size)
*  @brief  Deallocate
*  @param  (IN) void * p
*  @param  (IN) std::size_t size
*  @param  (OUT)
*  @return void
*/
void CSmallObjAllocator::Deallocate(void* p, std::size_t numBytes)
{
    if ( NULL == p ) return;
    if ( numBytes > GetMaxObjectSize() )
    {
        DefaultDeallocator( p );
        return;
    }
    assert( NULL != pool_ );
    if ( 0 == numBytes ) numBytes = 1;
    const std::size_t index = GetOffset( numBytes, GetAlignment() ) - 1;
    const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
    (void) allocCount;
    assert( index < allocCount );
    CFixedAllocator & allocator = pool_[ index ];
    assert( allocator.BlockSize() >= numBytes );
    assert( allocator.BlockSize() < numBytes + GetAlignment() );
    const bool found = allocator.Deallocate( p, NULL );
    (void) found;
    assert( found );
}
void CSmallObjAllocator::Deallocate(void* p)
{
    if ( NULL == p ) return;
    assert( NULL != pool_ );
    CFixedAllocator * pAllocator = NULL;
    const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
    CChunk * chunk = NULL;

    for ( std::size_t ii = 0; ii < allocCount; ++ii )
    {
        chunk = pool_[ ii ].HasBlock( p );
        if ( NULL != chunk )
        {
            pAllocator = &pool_[ ii ];
            break;
        }
    }
    if ( NULL == pAllocator )
    {
        DefaultDeallocator( p );
        return;
    }

    assert( NULL != chunk );
    const bool found = pAllocator->Deallocate( p, chunk );
    (void) found;
    assert( found );
}

/** @fn     CSmallObjAllocator::~CSmallObjAllocator()
*  @brief  ~CSmallObjAllocator
*  @param  (OUT)
*  @return
*/
CSmallObjAllocator::~CSmallObjAllocator()
{
    delete [] pool_;
}

bool CSmallObjAllocator::TrimExcessMemory( void )
{
    bool found = false;
    const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
    std::size_t i = 0;
    for ( ; i < allocCount; ++i )
    {
        if ( pool_[ i ].TrimEmptyChunk() )
            found = true;
    }
    for ( i = 0; i < allocCount; ++i )
    {
        if ( pool_[ i ].TrimChunkList() )
            found = true;
    }

    return found;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

SmallObject.h

#pragma once
#include "SmallObjAllocator.h"

template<typename T>
class CSingletonHolder
{
public:
    static T& Instance()
    {
        static T* pInstance = NULL;
        if (!pInstance)
        {
            pInstance = new T;
        }
        return *pInstance;
    }
private:
    CSingletonHolder(){}
};

#define DEFAULT_CHUNK_SIZE 4096  // 缺省大小,最大分配4M空间供block使用
#define MAX_SMALL_OBJECT_SIZE 256 // 每个对象大小为64byte,大于64的不建议使用内存池
#define LOKI_DEFAULT_OBJECT_ALIGNMENT 4 // 对齐大小

template<std::size_t chunkSize = DEFAULT_CHUNK_SIZE,
        std::size_t maxSmallObjectSize = MAX_SMALL_OBJECT_SIZE,
        std::size_t objectAlignSize = LOKI_DEFAULT_OBJECT_ALIGNMENT
>
class CSmallObjectSingleton: public CSmallObjAllocator
{
    typedef CSmallObjectSingleton<chunkSize, maxSmallObjectSize, objectAlignSize> MyAllocator;

    typedef CSingletonHolder<MyAllocator> MyAlloc;
    friend class CSingletonHolder<MyAllocator>;
public:
    /// Returns reference to the singleton.
    inline static CSmallObjectSingleton & Instance( void )
    {
        return MyAlloc::Instance();
    }
protected:
    // 构造
    CSmallObjectSingleton(void)
        : CSmallObjAllocator(chunkSize, maxSmallObjectSize, objectAlignSize)
    {

    }

    virtual ~CSmallObjectSingleton(void)
    {

    }
public:
    // 清空多余内存
    static void ClearExtraMemory( void )
    {
        Instance().TrimExcessMemory();
    }

    static void* operator new(std::size_t size)
    {
        return MyAlloc::Instance().Allocate( size);
    }

    static void operator delete(void* p, std::size_t size)
    {
        MyAlloc::Instance().Deallocate( p, size );
    }

    static void operator delete(void* p)
    {
        MyAlloc::Instance().Deallocate( p );
    }
};


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

简单使用举例

MyAllocation.h

#pragma once
#include "SmallObject.h"

typedef CSmallObjectSingleton<4096, 64, 4> SmallObjectParent;


class CMyAllocation: public SmallObjectParent
{
public:
    CMyAllocation(void);
    ~CMyAllocation(void);
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void playexe::OnTestClicked()
{
    CMyAllocation aa;
    char* pA1 = static_cast<char*>(aa.Allocate(21));
    char* pA2 = static_cast<char*>(aa.Allocate(21));
    char* pA3 = static_cast<char*>(aa.Allocate(21));
    aa.Deallocate(pA1);
    char* pA4 = static_cast<char*>(aa.Allocate(21));
    return;

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////







0 0
原创粉丝点击