Understanding Java Connection Pools: Commons Pool 2, Jedis, and HikariCP Performance

This article explains the principles of object pooling in Java, introduces the Commons Pool 2 library and its use in Redis client Jedis, compares pool and non‑pool performance with JMH benchmarks, and details the fast HikariCP database connection pool along with configuration tips and interview questions.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Understanding Java Connection Pools: Commons Pool 2, Jedis, and HikariCP Performance

In Java applications, creating and destroying expensive resources such as threads, database connections, or TCP sockets can significantly impact performance, so pooling these objects allows reuse after lightweight reset.

The Commons Pool 2 library provides a generic object pool implementation. Adding the Maven dependency:

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

The core class GenericObjectPool is created with a PooledObjectFactory and a GenericObjectPoolConfig:

public GenericObjectPool(final PooledObjectFactory<T> factory, final GenericObjectPoolConfig<T> config)

Jedis, a Redis client, uses Commons Pool 2 via JedisFactory. The key method makeObject creates a Jedis instance, connects it, and returns a DefaultPooledObject<Jedis>:

@Override
public PooledObject<Jedis> makeObject() throws Exception {
    Jedis jedis = null;
    try {
        jedis = new Jedis(jedisSocketFactory, clientConfig);
        jedis.connect();
        return new DefaultPooledObject<>(jedis);
    } catch (JedisException je) {
        if (jedis != null) {
            try { jedis.quit(); } catch (RuntimeException e) { logger.warn("Error while QUIT", e); }
            try { jedis.close(); } catch (RuntimeException e) { logger.warn("Error while close", e); }
        }
        throw je;
    }
}

The pool obtains objects via borrowObject, first trying the idle queue and creating a new instance if none are available:

public T borrowObject(final Duration borrowMaxWaitDuration) throws Exception {
    // omitted lines
    while (p == null) {
        create = false;
        p = idleObjects.pollFirst();
        if (p == null) {
            p = create();
            if (p != null) { create = true; }
        }
        // omitted lines
    }
    // omitted lines
}

Objects are stored in a LinkedBlockingDeque, a double‑ended queue.

The main configuration properties of GenericObjectPoolConfig include maxTotal, maxIdle, minIdle, maxWaitMillis, eviction settings, and test flags. Proper tuning of these parameters (especially maxTotal and maxWaitMillis) is crucial for performance and stability.

Interview questions often focus on appropriate timeout values; a typical recommendation is 500‑1000 ms for services that should respond within 10 ms under normal load.

A JMH benchmark comparing pooled versus non‑pooled Redis operations shows roughly a 5× throughput increase when using a pool:

@Fork(2)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@BenchmarkMode(Mode.Throughput)
public class JedisPoolVSJedisBenchmark {
    JedisPool pool = new JedisPool("localhost", 6379);

    @Benchmark
    public void testPool() {
        Jedis jedis = pool.getResource();
        jedis.set("a", UUID.randomUUID().toString());
        jedis.close();
    }

    @Benchmark
    public void testJedis() {
        Jedis jedis = new Jedis("localhost", 6379);
        jedis.set("a", UUID.randomUUID().toString());
        jedis.close();
    }
}

HikariCP, the default Spring Boot 2.x connection pool, achieves high performance through three main techniques:

Replacing ArrayList with FastList to reduce bounds checks.

Bytecode optimization using Javassist, swapping invokevirtual for invokestatic.

Implementing a lock‑free ConcurrentBag to minimize contention.

Key configuration parameters are maximumPoolSize and minimumIdle. In practice, 20‑50 database connections are sufficient for most workloads; setting the pool size excessively large can degrade performance.

HikariCP relies on Connection.isValid() for health checks, avoiding many of the test flags present in Commons Pool.

The article also introduces the concept of a "Result Cache Pool", where the result of expensive operations is cached similarly to object pooling, improving latency for repeated accesses.

In summary, object pooling (for Redis, JDBC, HTTP, RPC, etc.) reduces creation cost, improves throughput, and, when combined with proper configuration and monitoring, can dramatically enhance system performance.

Pool lifecycle diagram
Pool lifecycle diagram
Blocking behavior diagram
Blocking behavior diagram
HikariCP performance chart
HikariCP performance chart
Multiple pool example
Multiple pool example
Connection pool saturation graph
Connection pool saturation graph
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaperformanceConnection PoolJedisHikariCPJMHCommons-Pool
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

0 followers
Reader feedback

How this landed with the community

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.