Object pool pattern

Uses your system voice; voice and rate in settings.

Snapshot

≈ 6 min read

Names in other languages

No names in other languages are held for this entry yet.

Article bodies are English and are not machine-translated. A name in another language is not a translation of the article.

The object pool pattern is a software creational design pattern that uses a set of initialized objects kept ready to use – a "pool" – rather than allocating and destroying them on demand. A client of the pool will request an object from the pool and perform operations on the returned object. When the client has finished, it returns the object to the pool rather than destroying it; this can be done manually or automatically. Object pools are primarily used for performance: in some circumstances, object pools significantly improve performance. Object pools complicate object lifetime, as objects obtained from and returned to a pool are not actually created or destroyed at this time, and thus require care in implementation.

The object pool pattern is a software creational design pattern that uses a set of initialized objects kept ready to use – a "pool" – rather than allocating and destroying them on demand. A client of the pool will request an object from the pool and perform operations on the returned object. When the client has finished, it returns the object to the pool rather than destroying it; this can be done manually or automatically. Object pools are primarily used for performance: in some circumstances, object pools significantly improve performance. Object pools complicate object lifetime, as objects obtained from and returned to a pool are not actually created or destroyed at this time, and thus require care in implementation. In C++26, the C++ Standard Library introduces a new header with the data structure std::hive , which essentially implements an object pool. It is a collection that reuses erased elements' memory, and has a O(1) amortized iteration (insertion and erasure). Similar to the linked list collection std::list , pointers and iterators remain valid across insertion and erasure, but also provides strong cache locality through contiguous memory blocks. Its iteration is bidirectional. It does not specify insertion position, does not support random access ( operator[] / at() ), or comparison ( operator== , since insertion order itself is not specified). Along with it is a class std::hive_limits for layout information on block capacity limits. std::hive is based on plf::colony . import std; using std::hive; using std::string; using namespace std::literals; int main() { hive stringHive; // Insert stringHive.insert("foo"); stringHive.insert("bar"); // Remove std::erase(stringHive, "foo"s); // Iteration for (const string& value: stringHive) { std::println("{}", value); } return 0; } namespace Wikipedia.Examples; using System; using System.Collections.Generic; // The PooledObject class is the type that is expensive or slow to instantiate, // or that has limited availability, so is to be held in the object pool. public class PooledObject { private DateTime _createdAt = DateTime.Now; public DateTime CreatedAt => _createdAt; public string TempData { get; set; } } // The Pool class controls access to the pooled objects. It maintains a list of available objects and a // collection of objects that have been obtained from the pool and are in use. The pool ensures that released objects // are returned to a suitable state, ready for reuse. public static class Pool { private static List _available = new(); private static List _inUse = new(); public static PooledObject GetObject() { lock (_available) { if (_available.Count != 0) { PooledObject po = _available[0]; _inUse.Add(po); _available.RemoveAt(0); return po; } else { PooledObject po = new(); _inUse.Add(po); return po; } } } public static void ReleaseObject(PooledObject po) { CleanUp(po); lock (_available) { _available.Add(po); _inUse.Remove(po); } } private static void CleanUp(PooledObject po) { po.TempData = null; } } In the code above, the PooledObject has properties for the time it was created, and another, that can be modified by the client, that is reset when the PooledObject is released back to the pool. Shown is the clean-up process, on release of an object, ensuring it is in a valid state before it can be requested from the pool again. // package pool package pool import ( "errors" "log" "math/rand" "sync" "time" ) const getResMaxTime = 3 * time.Second var ( ErrPoolNotExist = errors.New("pool not exist") ErrGetResTimeout = errors.New("get resource time out") ) //Resource type Resource struct { resId int } // NewResource Simulate slow resource initialization creation // (e.g., TCP connection, SSL symmetric key acquisition, auth authentication are time-consuming) func NewResource(id int) *Resource { time.Sleep(500 * time.Millisecond) return &Resource{resId: id} } // Do Simulation resources are time consuming and random consumption is 0~400ms func (r *Resource) Do(workId int) { time.Sleep(time.Duration(rand.Intn(5)) * 100 * time.Millisecond) log.Printf("using resource #%d finished work %d finish\n", r.resId, workId) } // Pool based on Go channel implementation, to avoid resource race state problem type Pool chan *Resource // New a resource pool of the specified size // Resources are created concurrently to save resource initialization time func New(size int) Pool { p := make(Pool, size) wg := new(sync.WaitGroup) wg.Add(size) for i := 0; i package org.wikipedia.examples; public class PooledObject { private String temp1; private String temp2; private String temp3; public String getTemp1() { return temp1; } public void setTemp1(String temp1) { this.temp1 = temp1; } public String getTemp2() { return temp2; } public void setTemp2(String temp2) { this.temp2 = temp2; } public String getTemp3() { return temp3; } public void setTemp3(String temp3) { this.temp3 = temp3; } } In : package org.wikipedia.examples; import java.util.HashMap; import java.util.Map; public class PooledObjectPool { public static final long EXPIRY_TIME = 6000; // 6 seconds private static Map available = new HashMap<>(); private static Map inUse = new HashMap<>(); public synchronized static PooledObject getObject() { long now = System.currentTimeMillis(); if (!available.isEmpty()) { for (Map.Entry entry : available.entrySet()) { if (now - entry.getValue() > EXPIRY_TIME) { // object has expired popElement(available); } else { PooledObject po = popElement(available, entry.getKey()); push(inUse, po, now); return po; } } } // either no PooledObject is available or each has expired, so return a new one return createPooledObject(now); } private synchronized static PooledObject createPooledObject(long now) { PooledObject po = new PooledObject(); push(inUse, po, now); return po; } private synchronized static void push(HashMap map, PooledObject po, long now) { map.put(po, now); } public static void releaseObject(PooledObject po) { cleanUp(po); available.put(po, System.currentTimeMillis()); inUse.remove(po); } private static PooledObject popElement(HashMap map) { Map.Entry entry = map.entrySet().iterator().next(); PooledObject key = entry.getKey(); // Long value = entry.getValue(); map.remove(entry.getKey()); return key; } private static PooledObject popElement(HashMap map, PooledObject key) { map.remove(key); return key; } public static void cleanUp(PooledObject po) { po.setTemp1(null); po.setTemp2(null); po.setTemp3(null); } } use colony::Colony; fn main() { let mut colony: Colony = Colony::new(); // Insert let foo_handle = colony.insert("foo"); let bar_handle = colony.insert("bar"); // Remove assert_eq!(colony.remove(foo_handle), Some("foo")); // Lookup assert_eq!(colony.get(foo_handle), None); assert_eq!(colony.get(bar_handle), Some(&"bar")); // Iteration for (key, &value) in colony.iter() { assert_eq!((key, value), (bar_handle, "bar")); } } ==Notes== ==References==

Text derived from https://en.wikipedia.org/w/index.php?oldid=1373118714 under CC BY-SA 4.0; modifications have been made.