Object Pool Design Pattern

Last Updated : 13 May, 2026

The Object Pool Design Pattern is a creational pattern that manages a set of reusable objects to reduce the cost of repeatedly creating and destroying them. Instead of instantiating new objects, clients borrow objects from a pool and return them after use. This improves performance and resource utilization, especially when object creation is expensive.

  • Maintains a pool of pre-initialized reusable objects that are temporarily taken for use and returned after completion.
  • Reduces overhead of object creation and destruction, improving performance and memory efficiency.

Example: In a web application, every user request often needs to interact with a database (e.g., fetching user data, saving orders, etc.). However, creating a new database connection for every request is expensive.

419253541

In the Diagram

  • Clients request (borrow) a database connection from the pool instead of creating a new one.
  • The pool tracks connections as available or in-use while clients are using them.
  • After use, connections are returned to the pool, making them available for reuse.

Real-World Applications

The Object Pool Pattern is widely used in software systems to efficiently manage and reuse costly resources, improving performance and scalability.

  • Database Connection Pool: Web applications reuse database connections instead of creating a new one for each request, improving performance and reducing latency.
  • Thread Pool (Server Systems): Servers maintain a pool of threads to handle multiple requests efficiently without creating new threads every time.
  • HTTP Connection Pool: Applications reuse HTTP connections for API calls, avoiding repeated connection setup and improving response time.
  • Object Pool in Game Development: Games reuse objects like bullets, enemies, or particles instead of creating/destroying them repeatedly.

Components

The Object Pool Pattern consists of key elements that manage the creation, reuse, and lifecycle of pooled objects efficiently.

object_pool_design_pattern
UML Diagram

The main components of object pool design pattern:

  • Client : This is the class that uses an object of the PooledObject type.
  • ReusablePool: 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.
  • ObjectPool : The Pool class is the most important class in the object pool design pattern. ObjectPool maintains a list of available objects and a collection of objects that have already been requested from the pool.

Uses

The Object Pool Pattern is used in scenarios where object creation is costly and efficient reuse of resources is required.

  • Useful when object creation is expensive (e.g., database or network connections), allowing reuse instead of repeatedly creating new instances.
  • Helps manage limited resources efficiently by reusing objects, preventing exhaustion of system resources.
  • Reduces overhead by minimizing frequent object creation and destruction, improving overall system efficiency.

Object Pool Life Cycle

The lifecycle of objects in an object pool involves the following stages:

  • Stage 1: Creation: Objects are initially created and added to the pool.
  • Stage 2: Borrowing: Clients request and borrow objects from the pool.
  • Stage 3: Usage: Clients use the borrowed objects for their tasks.
  • Stage 4: Returning: After usage, clients return the objects to the pool for reuse.
  • Stage 5: Rejection or Destruction: If the pool is full or objects are not used, they may be rejected or removed from the pool.

Implementation Example

Problem Statement:

Take the example of the database connections. It's obviously that opening too many connections might affect the performance for several reasons: 

  • Creating a connection is an expensive operation.
  • When there are too many connections opened it takes longer to create a new one and the database server will become overloaded.

Here the object pool manages the connections and provide a way to reuse and share them. It can also limit the maximum number of objects that can be created.

1. Reusable Pool(PooledObject)

Java
import java.sql.Connection;
import java.sql.SQLException;

// This class represents the objects that are expensive to create or have limited availability
public class PooledObject {
    private Connection connection;

    public PooledObject(Connection connection) {
        this.connection = connection;
    }

    public Connection getConnection() {
        return connection;
    }

    public void close() {
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public boolean isClosed() {
        try {
            return connection.isClosed();
        } catch (SQLException e) {
            e.printStackTrace();
            return true;
        }
    }
}

2. Object Pool

Java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Hashtable;

// ObjectPool class maintains a list of available objects and a collection of objects that have already been requested from the pool
public abstract class ObjectPool<T> {
    private long deadTime;
    private Hashtable<T, Long> lock, unlock;

    public ObjectPool() {
        deadTime = 50000; // 50 seconds
        lock = new Hashtable<>();
        unlock = new Hashtable<>();
    }

    abstract T create();

    abstract boolean validate(T o);

    abstract void dead(T o);

    public synchronized T takeOut() {
        long now = System.currentTimeMillis();
        T t;
        if (unlock.size() > 0) {
            Enumeration<T> e = unlock.keys();
            while (e.hasMoreElements()) {
                t = e.nextElement();
                if ((now - unlock.get(t)) > deadTime) {
                    unlock.remove(t);
                    dead(t);
                    t = null;
                } else {
                    if (validate(t)) {
                        unlock.remove(t);
                        lock.put(t, now);
                        return t;
                    } else {
                        unlock.remove(t);
                        dead(t);
                        t = null;
                    }
                }
            }
        }
        t = create();
        lock.put(t, now);
        return t;
    }

    public synchronized void takeIn(T t) {
        lock.remove(t);
        unlock.put(t, System.currentTimeMillis());
    }
}

3. Client

Java
import java.sql.Connection;

// Client class that uses an object of the PooledObject type
public class Client {
    public static void main(String[] args) {
        // Create the ConnectionPool:
        JDBCConnectionPool pool = new JDBCConnectionPool(
                "org.hsqldb.jdbcDriver", "jdbc:hsqldb: //localhost/mydb",
                "sa", "password");

        // Get a connection:
        Connection con = pool.takeOut();
        // Return the connection:
        pool.takeIn(con);
    }
}

Complete code for the above example

The complete code of the above example is:

Java
import java.util.Enumeration;
import java.util.Hashtable;

abstract class ObjectPool<T> {
    private long deadTime;
    private Hashtable<T, Long> locked, unlocked;

    public ObjectPool()
    {
        deadTime = 50000; // 50 seconds
        locked = new Hashtable<>();
        unlocked = new Hashtable<>();
    }

    protected abstract T create();
    protected abstract boolean validate(T o);
    protected abstract void expire(T o);

    public synchronized T takeOut()
    {
        long now = System.currentTimeMillis();
        T t;

        if (!unlocked.isEmpty()) {
            Enumeration<T> e = unlocked.keys();
            while (e.hasMoreElements()) {
                t = e.nextElement();
                if ((now - unlocked.get(t)) > deadTime) {
                    unlocked.remove(t);
                    expire(t);
                }
                else {
                    if (validate(t)) {
                        unlocked.remove(t);
                        locked.put(t, now);
                        return t;
                    }
                    else {
                        unlocked.remove(t);
                        expire(t);
                    }
                }
            }
        }

        t = create();
        locked.put(t, now);
        return t;
    }

    public synchronized void takeIn(T t)
    {
        locked.remove(t);
        unlocked.put(t, System.currentTimeMillis());
    }
}

class ConnectionResource {
    private static int counter = 0;
    private int id;

    public ConnectionResource()
    {
        id = ++counter;
        System.out.println("Creating Resource #" + id);
    }

    public void use()
    {
        System.out.println("Using Resource #" + id);
    }

    public void close()
    {
        System.out.println("Closing Resource #" + id);
    }
}

class ConnectionPool
    extends ObjectPool<ConnectionResource> {

    @Override protected ConnectionResource create()
    {
        return new ConnectionResource();
    }

    @Override
    protected boolean validate(ConnectionResource o)
    {
        return o != null;
    }

    @Override protected void expire(ConnectionResource o)
    {
        o.close();
    }
}

public class Main {
    public static void main(String[] args)
    {

        ConnectionPool pool = new ConnectionPool();

        ConnectionResource r1 = pool.takeOut();
        r1.use();

        pool.takeIn(r1);

        ConnectionResource r2 = pool.takeOut();
        r2.use();

        pool.takeIn(r2);
    }
}

Output
Creating Resource #1
Using Resource #1
Using Resource #1

Advantages

The Object Pool pattern improves performance and efficient resource management. 

  • Reuses pre-created objects through a managed pool, reducing the need for frequent creation and destruction.
  • Provides a mechanism to borrow and return objects while tracking usage and availability.
  • Improves performance in resource-intensive applications by minimizing overhead and garbage collection.
  • Optimizes memory usage and controls the number of objects created at a time.

Disadvantages

Although efficient, the Object Pool pattern can introduce complexity and risks.

  • Risk of resource leakage if objects are not properly returned
  • May cause thread-safety issues in multi-threaded environments
  • Not useful for lightweight or inexpensive objects
  • Can increase memory usage if the pool size is too large
Comment