【问题标题】:memory pool running on O(1)在 O(1) 上运行的内存池
【发布时间】:2012-11-09 20:00:08
【问题描述】:

有没有办法设计一个可以在 O(1) 内分配和释放内存的 C++ 内存池(仅限特定类)?

假设我有 T 类,我想在需要时只分配 100*sizeof(T) 的块。但是,我该如何处理在块中删除特定对象时发生的碎片?我可以为每个插槽设置一个布尔值来判断插槽是否被占用,但是我需要一个算法来给我下一个空闲插槽。

是否有任何标准方法可以在 O(1) 中实现这一点?我想这是一个相当普遍的事情

编辑: 图片看看我的意思

【问题讨论】:

  • 我不明白这对我有什么帮助...也许我的问题不清楚?
  • 你想要free list之类的东西。

标签: c++ memory-management


【解决方案1】:

这个解决方案使用了额外的内存(可能是也可能不是你想要的),如果你尝试连续两次释放一个块,你也会遇到问题。

预分配足够的内存。将其分成块,每个对象一个。保留一个空闲块的列表。当你分配一个新对象时,从空闲块列表的顶部选择一个块。当您释放一个对象时,将其块附加到空闲块列表中。这两个操作都是 O(1)。

看起来像这样:

初始化:

char* mem = new char[CHUNK_SIZE * OBJ_COUNT];
std::list<char*> free_chunks;
for (char* c = mem, int i = 0; i < OBJ_COUNT; ++i)
{
   free_chunks.push_back(c);
   c += CHUNK_SIZE;
}

获取一个新的块进行分配:

   if(free_chunks.size() > 0)
   {
    char* c = free_chunks.back();
    free_chunks.pop_back();
    return c;
   }
   else{ // Error, not enough memory... }

释放后返回一个块:

free_chunks.push_back(c);

【讨论】:

    【解决方案2】:

    您可以使用 O(1) 对象检索来实现简单的对象池。但是您的对象需要具有指向下一个对象的内部指针。 在池的构造函数内部进行了一些预先计算:

    pool.reserve(capacity); //std::vector<Object*>
    for (int i = 0; i < capacity; ++i) {
        pool.push_back(new Object());
    }
    for (int i = 0; i < capacity-1; ++i) {
        pool[i]->setNext(pool[i+1].get());
    }
    first_available = pool[0].get(); //variable where you store first free object 
    pool[capacity-1]->setNext(NULL);
    

    那么getObject方法是:

    getObject* getObject()
    {
        getObject* obj = first_available;
        first_available = first_available->getNext();
        return obj;
    }
    
    void returnObject(Object* obj)
    {
        obj->setNext(first_available);
        first_available = obj;
    }
    

    希望对你有帮助。

    【讨论】:

      【解决方案3】:

      对于一般的内存分配问题,您只有碎片,其中请求任意长度的块。这里的问题是考虑到这种内存布局:

      @@@@@@---@--@-@@@@
      

      (其中@ 表示正在使用,- 表示免费)

      虽然总共有 6 个空闲块,但您不能分配 4 个连续块。所以你必须增加治理空间的总量(如果可能的话)。 同样在这种一般情况下,决定选择哪个地址并非易事,并且有几种影响碎片程度的策略(第一次拟合、最差拟合等)。

      在您的情况下,问题要容易得多,因为您知道要分配的区域将始终与某个整数 k(例如 k = sizeof(T))的大小完全相同。 这意味着您将始终拥有完美契合的积木,您可以随意组织这些积木。只需像处理链表一样处理空闲空间,并始终使用链表中的第一项来响应内存分配请求。

      @@@---@@@------@@@@@@@@@---@@@ (k=3)
      

      现在,如果您想在大块中分配以摊销分配,您可以保留一个额外的计数器来记录一个特定块中使用了多少个插槽,以便在该块为空时释放该块(if你想缩小你的插槽库!)。

      如果您坚持总是从最常用的块中分配新的内存块,您可以通过使用指示哪个块最满的附加列表来做到这一点。由于您始终只能将已使用插槽的数量更改为 1,因此您可以将该列表按O(1) 排序,只需交换相邻节点即可。

      【讨论】:

      • 我猜当对象的大小不同时,这在 O(1) 中是行不通的?
      • @lezebulon:我不知道。除非您愿意“四舍五入”以节省空间或有一些非常具体的对象大小。但一般问题总是会产生碎片化。
      猜你喜欢
      • 1970-01-01
      • 2016-09-09
      • 2018-05-28
      • 1970-01-01
      • 2012-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多