I do not think that a forced pool provides what you want. There are actually 4 other template parameters for boost::pool_allocator besides the type of object:
UserAllocator : Defines the method that the base pool will use to allocate memory from the system (default = boost::default_user_allocator_new_delete ).Mutex : Allows the user to determine the type of synchronization that will be used in the base singleton_pool (default = boost::details::pool::default_mutex ).NextSize : the value of this parameter is passed to the base pool when it is created and the number of blocks to allocate in the first distribution request is specified (default = 32).MaxSize : the value of this parameter is transferred to the base pool when it is created and indicates the maximum number of blocks to allocate in a single distribution request (default = 0).
You might think that MaxSize is exactly what you want, but unfortunately it is not. boost::pool_allocator uses the base boost::singleton_pool , which is based on boost::pool , will MaxSize eventually go to the boost::pool<> : max_size data element, and what role does max_size play in boost::pool ? look at boost::pool::malloc() :
void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION() { //! Allocates a chunk of memory. Searches in the list of memory blocks //! for a block that has a free chunk, and returns that free chunk if found. //! Otherwise, creates a new memory block, adds its free list to pool free list, //! \returns a free chunk from that block. //! If a new memory block cannot be allocated, returns 0. Amortized O(1). // Look for a non-empty storage if (!store().empty()) return (store().malloc)(); return malloc_need_resize(); }
Obviously, boost::pool immediately allocates a new memory block if there is no free fragment in the memory block . Keep digging in malloc_need_resize() :
template <typename UserAllocator> void * pool<UserAllocator>::malloc_need_resize() { //! No memory in any of our storages; make a new storage, //! Allocates chunk in newly malloc aftert resize. //! \returns pointer to chunk. size_type partition_size = alloc_size(); size_type POD_size = static_cast<size_type>(next_size * partition_size + math::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type)); char * ptr = (UserAllocator::malloc)(POD_size); if (ptr == 0) { if(next_size > 4) { next_size >>= 1; partition_size = alloc_size(); POD_size = static_cast<size_type>(next_size * partition_size + math::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type)); ptr = (UserAllocator::malloc)(POD_size); } if(ptr == 0) return 0; } const details::PODptr<size_type> node(ptr, POD_size); BOOST_USING_STD_MIN(); if(!max_size) next_size <<= 1; else if( next_size*partition_size/requested_size < max_size) next_size = min BOOST_PREVENT_MACRO_SUBSTITUTION(next_size << 1, max_size*requested_size/ partition_size); // initialize it, store().add_block(node.begin(), node.element_size(), partition_size); // insert it into the list, node.next(list); list = node; // and return a chunk from it. return (store().malloc)(); }
As you can see from the source code, max_size simply related to the number of blocks for a request from the system the next time , we can only slow down the rate of increase with this parameter.
But note that we can determine the method that the base pool will use to allocate memory from the system; if we limit the size of memory allocated from the system, the pool size will not grow. In this way, boost::pool seems redundant; you can directly pass the custom allocator to the STL container. Here is an example of a custom allocator (based on this link ) that allocates memory from the stack to a given size:
#include <cassert> #include <iostream> #include <vector> #include <new> template <std::size_t N> class arena { static const std::size_t alignment = 8; alignas(alignment) char buf_[N]; char* ptr_; bool pointer_in_buffer(char* p) noexcept { return buf_ <= p && p <= buf_ + N; } public: arena() noexcept : ptr_(buf_) {} ~arena() { ptr_ = nullptr; } arena(const arena&) = delete; arena& operator=(const arena&) = delete; char* allocate(std::size_t n); void deallocate(char* p, std::size_t n) noexcept; static constexpr std::size_t size() { return N; } std::size_t used() const { return static_cast<std::size_t>(ptr_ - buf_); } void reset() { ptr_ = buf_; } }; template <std::size_t N> char* arena<N>::allocate(std::size_t n) { assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena"); if (buf_ + N - ptr_ >= n) { char* r = ptr_; ptr_ += n; return r; } std::cout << "no memory available!\n"; return NULL; } template <std::size_t N> void arena<N>::deallocate(char* p, std::size_t n) noexcept { assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena"); if (pointer_in_buffer(p)) { if (p + n == ptr_) ptr_ = p; } } template <class T, std::size_t N> class short_alloc { arena<N>& a_; public: typedef T value_type; public: template <class _Up> struct rebind { typedef short_alloc<_Up, N> other; }; short_alloc(arena<N>& a) noexcept : a_(a) {} template <class U> short_alloc(const short_alloc<U, N>& a) noexcept : a_(a.a_) {} short_alloc(const short_alloc&) = default; short_alloc& operator=(const short_alloc&) = delete; T* allocate(std::size_t n) { return reinterpret_cast<T*>(a_.allocate(n*sizeof(T))); } void deallocate(T* p, std::size_t n) noexcept { a_.deallocate(reinterpret_cast<char*>(p), n*sizeof(T)); } template <class T1, std::size_t N1, class U, std::size_t M> friend bool operator==(const short_alloc<T1, N1>& x, const short_alloc<U, M>& y) noexcept; template <class U, std::size_t M> friend class short_alloc; }; template <class T, std::size_t N, class U, std::size_t M> inline bool operator==(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept { return N == M && &x.a_ == &y.a_; } template <class T, std::size_t N, class U, std::size_t M> inline bool operator!=(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept { return !(x == y); } int main() { const unsigned N = 1024; typedef short_alloc<int, N> Alloc; typedef std::vector<int, Alloc> SmallVector; arena<N> a; SmallVector v{ Alloc(a) }; for (int i = 0; i < 400; ++i) { v.push_back(10); } }