Backend Development 14 min read

Object Pool Pattern: Principles, Apache Commons Pool Implementation, and Practical Use Cases

This article explains the object pool design pattern, its working mechanism, advantages and disadvantages, and provides step‑by‑step Java code using Apache Commons Pool along with real‑world examples for web servers and game development to improve performance and resource management.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Object Pool Pattern: Principles, Apache Commons Pool Implementation, and Practical Use Cases

Hello everyone, I'm Chen.

The object pool pattern is a widely used design pattern in software development that improves application performance and efficiency by reusing objects whose creation is costly. It is especially useful when creating new instances is time‑consuming and the creation frequency is high, or when the number of instances is limited by resources.

Work Mechanism

The object pool works by creating a pre‑initialized pool of objects that can be borrowed and returned as needed. Instead of creating a new object each time, the pool is searched for an available reusable object. If one is found, it is removed from the pool and handed to the requester; otherwise a new object is created and added to the pool.

Code Implementation of Object Pool

I use Apache Commons Pool to implement object pooling.

1. Add dependency

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.9.0</version>
</dependency>

2. Example of the object to be pooled

public class Foo {

    private final String username;

    public Foo(String username) {
        this.username = username;
    }

    public String getUsername() {
        return username;
    }
}

3. Build a factory for object creation by implementing org.apache.commons.pool2.PooledObjectFactory<T> (or extending its abstract class) to handle creation, wrapping, destruction, etc.

public class FooPoolObjectFactory extends BasePooledObjectFactory<Foo> {

    @Override
    public Foo create() throws Exception {
        return new Foo(String.valueOf(RandomUtils.randomInt(0, 10)));
    }

    @Override
    public PooledObject<Foo> wrap(Foo obj) {
        return new DefaultPooledObject<>(obj);
    }
}

4. Implement an eviction policy to periodically check the health of objects and discard unusable ones. For example, database connections need heartbeat checks.

public class FooEvictionPolicy implements EvictionPolicy<Foo> {

    @Override
    public boolean evict(EvictionConfig config, PooledObject<Foo> underTest, int idleCount) {
        // TODO: periodically verify object functionality
        return true;
    }
}

5. Build and configure the object pool

public GenericObjectPool<Foo> fooGenericObjectPool() {
    GenericObjectPoolConfig<Foo> poolConfig = new GenericObjectPoolConfig<>();
    poolConfig.setEvictionPolicy(new FooEvictionPolicy());
    poolConfig.setBlockWhenExhausted(true);
    poolConfig.setJmxEnabled(false);
    poolConfig.setMaxWaitMillis(1000 * 10);
    poolConfig.setTimeBetweenEvictionRunsMillis(60 * 1000);
    poolConfig.setMinEvictableIdleTimeMillis(20 * 1000);
    poolConfig.setTestWhileIdle(true);
    poolConfig.setTestOnReturn(true);
    poolConfig.setTestOnBorrow(true);
    poolConfig.setMaxTotal(3);
    // abandoned object removal configuration
    AbandonedConfig abandonedConfig = new AbandonedConfig();
    abandonedConfig.setRemoveAbandonedOnMaintenance(true);
    abandonedConfig.setRemoveAbandonedOnBorrow(true);
    return new GenericObjectPool<>(new FooPoolObjectFactory(), poolConfig, abandonedConfig);
}

6. Borrow and return objects

private final GenericObjectPool<Foo> fooGenericObjectPool = fooGenericObjectPool();

public Foo borrowFoo() throws Exception {
    return fooGenericObjectPool.borrowObject();
}

public void returnObject(Foo foo) {
    fooGenericObjectPool.returnObject(foo);
}

Advantages of Object Pool

Improved Performance : Reduces overhead of object creation and destruction by reusing pre‑initialized objects.

Resource Management : Controls shared resources such as database connections or file handles, preventing exhaustion.

Consistency : Guarantees objects are in a known state before use, useful for expensive initialization.

Ease of Implementation : The pattern is well‑known and straightforward to apply in many languages.

Disadvantages of Object Pool

Increased Complexity : Adds an abstraction layer that can make code harder to understand and maintain.

Overhead : Managing the pool itself consumes resources; improper sizing can lead to high overhead.

Limited Flexibility : Fixed‑size pools may not suit scenarios requiring dynamic scaling.

Thread‑Safety : Concurrent access requires synchronization, adding extra cost.

Resource Leaks : Failure to return objects can cause leaks and eventual resource exhaustion.

Application Scenarios

Typical objects that benefit from pooling are heavyweight and expensive to create, such as threads, database connections, TCP sockets, or FTP connections.

1. Web Server Example

Web servers handle many concurrent requests; pooling database connections, sockets, etc., improves performance and scalability.

import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class ConnectionPool {
    private static final int MAX_POOL_SIZE = 10;
    private static final int MAX_WAIT_TIME = 5000; // milliseconds
    private static final int PORT_NUMBER = 8080;

    private final BlockingQueue
pool;
    private final ServerSocket serverSocket;

    public ConnectionPool() throws Exception {
        pool = new ArrayBlockingQueue<>(MAX_POOL_SIZE);
        serverSocket = new ServerSocket(PORT_NUMBER);
        System.out.println("Server started on port " + PORT_NUMBER);
    }

    public Socket getConnection() throws Exception {
        Socket connection = pool.poll();
        if (connection == null) {
            try {
                connection = serverSocket.accept();
                System.out.println("New connection accepted from " + connection.getInetAddress());
            } catch (SocketTimeoutException e) {
                System.out.println("Timeout waiting for connection. No connection found within " + MAX_WAIT_TIME + " milliseconds.");
            }
        }
        return connection;
    }

    public void returnConnection(Socket connection) {
        if (pool.size() < MAX_POOL_SIZE) {
            pool.offer(connection);
            System.out.println("Connection returned to pool. Pool size is now " + pool.size());
        } else {
            try {
                connection.close();
                System.out.println("Connection pool is full. Discarded connection.");
            } catch (Exception e) {
                System.out.println("Error closing discarded connection.");
            }
        }
    }

    public static void main(String[] args) throws Exception {
        ConnectionPool connectionPool = new ConnectionPool();
        while (true) {
            Socket connection = connectionPool.getConnection();
            // Do some work with the connection
            Thread.sleep(5000);
            connectionPool.returnConnection(connection);
        }
    }
}

The ConnectionPool class manages a pool of network connections, initializing up to 10 connections and listening on port 8080.

2. Game Development Example

Games often need to create and destroy many short‑lived objects (particles, bullets, enemies). Pooling these objects improves performance.

import java.util.ArrayList;
import java.util.List;

public class GameObjectPool {

    class GameObject {
        public void reset() {
            // reset object to default state
        }
    }

    private static final int MAX_POOL_SIZE = 10;
    private final List
pool;

    public GameObjectPool() {
        pool = new ArrayList<>(MAX_POOL_SIZE);
        for (int i = 0; i < MAX_POOL_SIZE; i++) {
            pool.add(new GameObject());
        }
    }

    public GameObject getObject() {
        GameObject gameObject = pool.remove(0);
        gameObject.reset();
        return gameObject;
    }

    public void returnObject(GameObject gameObject) {
        if (pool.size() < MAX_POOL_SIZE) {
            pool.add(gameObject);
        }
    }

    public static void main(String[] args) {
        GameObjectPool gameObjectPool = new GameObjectPool();
        // Use game objects from pool
        GameObject gameObject1 = gameObjectPool.getObject();
        // modify gameObject1
        gameObjectPool.returnObject(gameObject1);
        GameObject gameObject2 = gameObjectPool.getObject();
        // modify gameObject2
        gameObjectPool.returnObject(gameObject2);
    }
}

The GameObjectPool class manages a pool of GameObject instances, initializing a fixed size of 10 and providing methods to borrow and return objects.

Summary

In conclusion, the object pool pattern is a powerful design pattern that can significantly improve application performance and efficiency by reusing expensive objects. It offers a mechanism for managing shared resources and prevents resource exhaustion by limiting the number of created objects. When applied correctly, it becomes an effective tool for enhancing the scalability and reliability of software applications.

Final Note (Please Support)

If this article helped you, please like, view, share, or bookmark it. Your support motivates me to keep producing content.

Additionally, I have opened a knowledge community where you can get discounted courses on Spring, MyBatis, DDD micro‑services, and more. Reply with the keyword to receive a coupon.

Follow the public account "Code Monkey Technical Column" for more fan benefits and join the discussion group by replying "join group".

Javaperformanceresource managementdesign patternObject poolApache Commons Pool
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.