【问题标题】:Implement own memory pool实现自己的内存池
【发布时间】:2012-07-31 21:37:24
【问题描述】:

我想预先分配一定数量的内存并将该内存用于程序的其余部分。该程序基本上将为一些字符串和结构分配内存。我该如何实施?哪些数据结构用于存储指针,我如何使用它来给我一个特定的数量?

例如,如果我 malloc 1 MB 的空间并将其放在指针 p 中,我如何从中分割出 250 KB 的空间?

这只是一个快速而肮脏的实现。

【问题讨论】:

  • 有无数种方法可以做到这一点,以及内存开销与速度等的各种权衡。一个简单的方法是维护一个空闲内存块的链表。列表中的每个条目都是一个地址和长度。当您进行分配/释放时,您会更新空闲列表。
  • 如何合并两个内存块或将一个块拆分为两个?
  • 您可以从开源实现开始,例如this one
  • 好吧,如果你这样做 char *a = malloc(1000000); 那么你可以这样做 char *b = &a[250000] , char *c = &a[500000] char *d = &a[750000] 。现在指针abcd 每个都指向一个250kB 的内存块。与任何其他缓冲区一样,您需要确保不会越过这些块访问。

标签: c memory-management


【解决方案1】:

如果您希望能够将内存返回到池中,它会变得更加复杂。但是,对于快速且不太脏的方法,您可能希望实现一些可以再次使用的代码...

typedef struct pool
{
  char * next;
  char * end;
} POOL;

POOL * pool_create( size_t size ) {
    POOL * p = (POOL*)malloc( size + sizeof(POOL) );
    p->next = (char*)&p[1];
    p->end = p->next + size;
    return p;
}

void pool_destroy( POOL *p ) {
    free(p);
}

size_t pool_available( POOL *p ) {
    return p->end - p->next;
}

void * pool_alloc( POOL *p, size_t size ) {
    if( pool_available(p) < size ) return NULL;
    void *mem = (void*)p->next;
    p->next += size;
    return mem;
}

根据我的经验,当使用这样的池分配许多对象时,我想预先计算需要多少内存,这样我就不会浪费,但我也不想犯任何错误(比如不分配够了)。所以我将所有分配代码放在一个循环中,并设置我的池分配函数来接受一个在空池上执行“虚拟”分配的标志。第二次循环时,我已经计算了池的大小,因此我可以创建池并使用相同的函数调用进行真正的分配,并且没有重复的代码。您需要更改我建议的池代码,因为如果尚未分配内存,则无法使用指针算法执行此操作。

【讨论】:

  • 嗯,要求“快速而肮脏” =) 制作 pool_alloc_aligned() 函数非常简单。
  • 你不认为 p->end = (char*)p + size + sizeof(pool) - 1;
  • @dexterous_stranger No. p-&gt;next 已经在(char*)p + sizeof(pool),并且p-&gt;end 应该是最后一个可用字节之后的一个,这样pool_available 才能工作。
  • 如何扩展它以使池在超出大小时可增长?
  • 可能是池的链接列表?
【解决方案2】:

使用内存池进行内存管理 -

内存池是预先分配相同大小的内存块的方式。 例如,同一类的各种对象。因此,更多的是设计软件的“内存模型”。

示例 - 动画 gif 具有各种帧。假设每个帧只需要最大 1024 KB。 另外,如果我们知道最多只能有两个帧,那么我们可以通过为每个帧预先分配内存来避免碎片。

[note] - 内存池更适用于我们在设计时了解系统行为的情况。 因此,内存池的概念并不适用于任何地方。 //================================================= ============================== // 名称:MemoryPool.cpp // 作者 : // 版本 : // 版权所有 : SHREYAS JOSHI // 描述 : //================================================= ==============================

#include <iostream>
#include <malloc.h>

struct memPool
{

private:

    char *m_poolPtr;
    char *m_nextAvailAddr;
    char *m_endAddr;

public:
    /** Methods for the structure **/
    void poolCreate(size_t size);
    void poolDestroy();
    void * poolAlloc(size_t size);

    memPool():m_poolPtr(NULL),m_nextAvailAddr(NULL),m_endAddr(NULL)
    {
        std::cout<<"memPool constructor Invoked"<<std::endl;
    }

    ~memPool()
    {
        std::cout<<"memPool Destructor Invoked"<<std::endl;
        m_poolPtr = NULL;
        m_nextAvailAddr = NULL;
        m_endAddr = NULL;
    }

};

/** Create a Pool of memory - makes a program hassle free of doing malloc multiple times **/
/** Also, Fragmentation can be avoided with the Memory Pool concept **/
/** A pool concept is useful, when you know at design time.how much memory is required for
the similar type of objects in total**/

void memPool::poolCreate(size_t size)
{
    m_poolPtr = (char *) malloc(size);

    if(m_poolPtr == NULL)
    {
        std::cout<<"Pool Create Failed"<<std::endl;
        //printf("Pool Create Failed \r\n");
    }

    m_nextAvailAddr = m_poolPtr;
    /** note the addressing starts from zero - thus you have already counted zero**/
    m_endAddr = m_poolPtr + size - 1;

    //printf("The Pool Head Pointer = %p \r\n",m_poolPtr);
    std::cout<<"Pool Head Pointer = "<<static_cast<void *>(m_poolPtr)<<std::endl;
    //printf("The Pool m_nextAvailAddr = %p \r\n",m_nextAvailAddr);
    std::cout<<"Pool m_nextAvailAddr = "<<static_cast<void *>(m_nextAvailAddr)<<std::endl;
    //printf("The Pool m_endAddr = %p \r\n",m_endAddr);
    std::cout<<"Pool m_endAddr = "<<static_cast<void *>(m_endAddr)<<std::endl;
}


/** Destroy the entire pool in one shot ********/
void memPool::poolDestroy()
{
    free(m_poolPtr);
    /** Remember free first then assign to NULL **/
    m_poolPtr = NULL;

    /** Update Housekeeping--data structure **/
    m_nextAvailAddr = NULL;
    m_endAddr = NULL;
}

/** Allocate some space from the pool ********/
/** Check if the space is available or not **/
/** Do the housekeeping - update the nextAvail Addr in the structure**/
void * memPool::poolAlloc(size_t size)
{
    void *mem = NULL;

    if( (m_endAddr != NULL) && (m_nextAvailAddr != NULL))
    {

        /** This is according to fencing problem - add 1 when you are find a difference of sequence to calculate the space within **/
        size_t availableSize = m_endAddr - m_nextAvailAddr + 1;

        /** check for the availability **/
        if(size > availableSize )
        {
            //std::cout<<"Warning!! the available size = "<<availableSize<< "requested size = "<<size<<std::endl;
            printf("Warning!! the available size = %u and requested size = %u \r\n",availableSize, size);
            mem = NULL;
        }
        else
        {
            /** store the available pointer to the user**/
            mem = m_nextAvailAddr;
            //printf("The user return pointer is = %p \r\n ",mem);
            std::cout<<"The user return pointer is = "<<static_cast <void *>(mem)<<std::endl;
            /*** advance the next available pointer **/
            m_nextAvailAddr += size;
            //printf("The next available pointer is = %p \r\n ",m_nextAvailAddr);
            std::cout<<"The next available pointer is = "<<static_cast<void *>(m_nextAvailAddr)<<std::endl;
        }

    }

    return mem;
}

int main(int argc, char *argv[])
{
    memPool gifAnimatedImageFramesBlk;

    /** Let's say each frame needs 512 kb **/
    char *gifFrame1 = NULL;
    char *gifFrame2 = NULL;

    char *gifFrame3 = NULL;

    /** 1 MB Pool for the GIF IMAGE FRAMES **/
    gifAnimatedImageFramesBlk.poolCreate(1024*1024*1024);
    /*** 512 KB **/
    gifFrame1 = (char *)gifAnimatedImageFramesBlk.poolAlloc(512*1024*1024);
    //printf("Got the gifFrame1..pointer- == %p \r\n ",gifFrame1);
    std::cout<<"Got the gifFrame1..pointer- == "<<static_cast<void *>(gifFrame1)<<std::endl;


    /** again 512 MB **/
    gifFrame2 = (char *)gifAnimatedImageFramesBlk.poolAlloc(512*1024*1024);

    std::cout<<"Got the gifFrame2..pointer- == "<<static_cast<void *>(gifFrame2)<<std::endl;

    //printf("Got the gifFrame2..pointer- == %p \r\n ",gifFrame2);

    /*************Exhausted the pool memory now **************/

    /** This will fail ****************/
    gifFrame3 = (char *)gifAnimatedImageFramesBlk.poolAlloc(1);

    std::cout<<"Got the gifFrame3..pointer- == "<<static_cast<void *>(gifFrame3)<<std::endl;
    //printf("Got the gifFrame3..pointer- == %p \r\n ",gifFrame3);

    /*****Destroy the Pool now *****************/
    gifAnimatedImageFramesBlk.poolDestroy();

    gifFrame3 = (char *)gifAnimatedImageFramesBlk.poolAlloc(1);

    std::cout<<"Got the gifFrame3..pointer- == "<<static_cast<void *>(gifFrame3)<<std::endl;

    //printf("Got the gifFrame3..pointer- == %p \r\n ",gifFrame3);

    gifFrame3 = (char *)gifAnimatedImageFramesBlk.poolAlloc(1);

    std::cout<<"Got the gifFrame3..pointer- == "<<static_cast<void *>(gifFrame3)<<std::endl;
    //printf("Got the gifFrame3..pointer- == %p \r\n ",gifFrame3);


    return 0;
}

[note] - 为了在 C++ 中使用 ostream::operator

内存池的优点

  1. 您可以避免内存碎片。即使系统需要内存空间,当所需的连续块大小不可用时,malloc() 也会失败。
  2. 预留空间,避免频繁使用malloc()和free()。这将节省时间。
  3. 当为许多子块调用 malloc() 时,管理/元数据与每个分配的子块相关联。这会 消耗不必要的空间。相反,一个大块分配将避免多个管理/元数据。
  4. 如果内存空间受到限制,那么很容易调查内存泄漏。如果 Pool 中的内存耗尽,则内存池将 返回 NULL。因此,您可以轻松隔离内存泄漏问题。

【讨论】:

  • 该问题仅标记为C
猜你喜欢
  • 1970-01-01
  • 2012-02-15
  • 2014-01-01
  • 2011-10-27
  • 2014-05-09
  • 2020-10-25
  • 2021-12-23
  • 1970-01-01
相关资源
最近更新 更多