【问题标题】:what's the difference between boost::pool<>::malloc and boost::pool<>::ordered_malloc, and when should I use boost::pool<>::ordered_malloc?boost::pool<>::malloc 和 boost::pool<>::ordered_malloc 有什么区别,什么时候应该使用 boost::pool<>::ordered_malloc?
【发布时间】:2013-04-03 05:57:49
【问题描述】:

我正在使用 boost.pool,但不知道何时使用boost::pool&lt;&gt;::mallocboost::pool&lt;&gt;::ordered_malloc

所以,

  1. boost::pool&lt;&gt;::mallocboost::pool&lt;&gt;::ordered_malloc有什么区别?

  2. 什么时候应该使用boost::pool&lt;&gt;::ordered_malloc

【问题讨论】:

    标签: boost pool


    【解决方案1】:

    首先,我们应该知道Boost Pool库背后的基本思想:simple_segregated_storage,它类似于单链表,负责将内存块划分为固定大小的块:

    内存池保存一个空闲的内存块列表。所以我们提到了块和块:内存池使用newmalloc来分配一个内存块,并将它分成许多大小相同的内存块。
    假设地址为8对齐,4个字节用于存储下一个块的地址,那么一个内存块(8字节* 32个块)如下(内存地址只是为了说明问题,不是真实的):

    现在,假设用户分配了两次 8 字节内存,因此使用了块:[0xDD00,0xDD08), [0xDD08,0xDD10)。一段时间后,用户在 [0xDD00,0xDD08) 处释放内存,因此该块将回到空闲列表。现在块是这样的:


    之后用户在 [0xDD08,0xDD10) 处释放内存,将此块放回列表中的最简单方法是更新 first 以指向它,恒定时间复杂度。 simple_segregated_storage&lt;T&gt;::free() 正是这样做的:

    void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const chunk)
    { //! Free a chunk.
      //! \pre chunk was previously returned from a malloc() referring to the same free list.
      //! \post !empty()
       BOOST_POOL_VALIDATE_INTERNALS
      nextof(chunk) = first;
      first = chunk;
      BOOST_POOL_VALIDATE_INTERNALS
    }
    

    之后,列表将如下所示:

    现在我们注意到,在这些操作之后,块列表没有按地址排序! 如果我们想在取消分配时保留顺序,请调用 pool&lt;&gt;::ordered_free() 而不是 pool&lt;&gt;::free() 以将内存按正确顺序放回列表中。现在我们已经知道内存池中的顺序是什么了,我们来深入研究boost::pool&lt;&gt;::mallocboost::pool&lt;&gt;::ordered_malloc的源码:

    void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION()
    {
      if (!store().empty())
        return (store().malloc)();
      return malloc_need_resize();
    }
    
    void * ordered_malloc()
    {
      if (!store().empty())
        return (store().malloc)();
      return ordered_malloc_need_resize();
    }
    

    正如我们所见,它们仅在内存块列表中没有空闲块时有所不同。在这种情况下,它分配一个新的内存块,将其空闲列表合并到池的空闲列表中,这两种方法的区别在于boost::pool&lt;&gt;::ordered_malloc在合并空闲列表时保留了顺序。
    以上是问题1。
    那么,为什么顺序很重要?!似乎内存池与无序块完美配合!
    首先,如果我们想找到 n 个块的连续序列,有序的空闲列表会更容易。其次,我们看一下boost::pool的派生类:boost::object_pool,它提供了在object_pool对象销毁时自动销毁未释放对象的功能,同时您也可以手动销毁该对象,例如:

    class X { … };
    
        void func()
        {
            boost::object_pool<X> alloc;
    
            X* obj1 = alloc.construct();
            X* obj2 = alloc.construct();
            alloc.destroy(obj2);
        }
    

    上面的代码是可以的,没有内存泄漏或双重删除! boost::object_pool 是如何做到这一点的?让我们找到boost::object_pool的析构函数的实现(我的机器上有boost 1.48):

    template <typename T, typename UserAllocator>
    object_pool<T, UserAllocator>::~object_pool()
    {
    #ifndef BOOST_POOL_VALGRIND
      // handle trivial case of invalid list.
      if (!this->list.valid())
        return;
    
      details::PODptr<size_type> iter = this->list;
      details::PODptr<size_type> next = iter;
    
      // Start 'freed_iter' at beginning of free list
      void * freed_iter = this->first;
    
      const size_type partition_size = this->alloc_size();
    
      do
      {
        // increment next
        next = next.next();
    
        // delete all contained objects that aren't freed.
    
        // Iterate 'i' through all chunks in the memory block.
        for (char * i = iter.begin(); i != iter.end(); i += partition_size)
        {
          // If this chunk is free,
          if (i == freed_iter)
          {
            // Increment freed_iter to point to next in free list.
            freed_iter = nextof(freed_iter);
    
            // Continue searching chunks in the memory block.
            continue;
          }
    
          // This chunk is not free (allocated), so call its destructor,
          static_cast<T *>(static_cast<void *>(i))->~T();
          // and continue searching chunks in the memory block.
        }
    
        // free storage.
        (UserAllocator::free)(iter.begin());
    
        // increment iter.
        iter = next;
      } while (iter.valid());
    
      // Make the block list empty so that the inherited destructor doesn't try to
      // free it again.
      this->list.invalidate();
    #else
       // destruct all used elements:
       for(std::set<void*>::iterator pos = this->used_list.begin(); pos != this->used_list.end(); ++pos)
       {
          static_cast<T*>(*pos)->~T();
       }
       // base class will actually free the memory...
    #endif
    }
    

    它遍历内存块列表中的所有块(listboost::pool&lt;&gt; 的数据成员,保存从系统分配的所有内存块的位置和大小)以查找其中是否还有块显示在空闲列表中,如果没有,则调用对象的析构函数,然后释放内存。所以这有点像std::set_intersection() 所做的那样,得到两组的交集!如果列表是排序的,这样做会快得多。其实在boost::object_pool&lt;&gt;中,需要顺序,公共成员函数:boost::object_pool&lt;&gt;::malloc()boost::object_pool&lt;&gt;::free()只需分别调用boost::pool&lt;&gt;::ordered_malloc()boost::pool&lt;&gt;::ordered_free()即可:

    element_type * malloc BOOST_PREVENT_MACRO_SUBSTITUTION()
    { //! Allocates memory that can hold one object of type ElementType.
      //!
      //! If out of memory, returns 0. 
      //!
      //! Amortized O(1).
      return static_cast<element_type *>(store().ordered_malloc());
    }
    void free BOOST_PREVENT_MACRO_SUBSTITUTION(element_type * const chunk)
    { //! De-Allocates memory that holds a chunk of type ElementType.
      //!
      //!  Note that p may not be 0.\n
      //!
      //! Note that the destructor for p is not called. O(N).
      store().ordered_free(chunk);
    }
    

    所以对于问题 2:在大多数情况下,您不需要使用 boost::pool&lt;&gt;::ordered_malloc

    【讨论】:

      猜你喜欢
      • 2012-01-07
      • 2013-05-16
      • 1970-01-01
      • 1970-01-01
      • 2018-07-21
      • 1970-01-01
      • 2019-10-20
      • 2014-09-02
      • 1970-01-01
      相关资源
      最近更新 更多