Learn when pooling is actually justified in Clojure, why pooling ordinary immutable values is usually a mistake, and how to manage scarce reusable resources safely when pooling does make sense.
Object pool: A bounded set of reusable objects or resources that callers borrow temporarily and then return for later reuse.
Object pooling is one of those techniques that sounds universally useful and is actually highly situational. In Clojure, it is especially easy to overuse because ordinary immutable values are usually cheap, short-lived, and safe to recreate. That means the first important rule is:
Pooling makes the most sense when the thing being reused is genuinely expensive or scarce:
Pooling ordinary immutable values often causes more trouble than it saves:
For most application data, better wins come from:
A good mental model is that pooling is about scarcity or setup cost, not nostalgia for manual memory management.
Good candidates:
Poor candidates:
If you do pool something mutable, the design must answer three questions clearly:
Without those answers, the pool often becomes a source of:
1(import '(java.util.concurrent ArrayBlockingQueue TimeUnit))
2
3(defn make-buffer-pool [pool-size buffer-size]
4 (let [pool (ArrayBlockingQueue. pool-size)]
5 (dotimes [_ pool-size]
6 (.put pool (byte-array buffer-size)))
7 pool))
8
9(defn with-buffer [pool f]
10 (let [buf (.poll pool 100 TimeUnit/MILLISECONDS)]
11 (when-not buf
12 (throw (ex-info "No buffer available" {})))
13 (try
14 (f buf)
15 (finally
16 (java.util.Arrays/fill buf (byte 0))
17 (.offer pool buf)))))
This example works because the pool is:
When a pool is empty, the system needs a deliberate answer:
That choice should reflect the real workload. If the pool protects a scarce resource, allowing unlimited fallback allocation may defeat the whole point.
For common infrastructure resources such as database connections or HTTP clients, it is usually better to use mature libraries and platform components rather than inventing a custom pool in application code.
The Clojure lesson is not “never pool.” It is “pool only when the semantics, ownership, and failure behavior are explicit.”
That usually adds complexity without reducing meaningful cost.
The next borrower may observe stale data.
The pool then turns into a surprise latency source.
That often defeats the resource budget the pool was meant to enforce.
Pool scarce or expensive-to-initialize resources, not ordinary immutable application data. Make ownership, reset rules, and exhaustion behavior explicit. Prefer mature libraries for common infrastructure pools, and treat custom pooling as a narrow low-level optimization rather than a default Clojure style. In most cases, simpler allocation discipline beats homegrown object reuse.