That pooling actually makes program performance worse especially in
concurrent applications, and it is advisable to instantiate new
objects instead, since in newer JVMs, instantiation of an object is
My knowledge of how the internal garbage collector works in Java is limited, but I've been reading papers lately that shed some light on the subject.
The way Java allocates objects initially uses a strategy similar to a sequential memory pool. Sequential pool allocators tend to be extremely fast at a rapid burst allocation strategy of objects. For example, in C, one can often see anywhere from a 2x to 10x improvement in constructing a complex tree if the nodes are allocated using a sequential allocator rather than a general allocator (
That's the initial phase of allocation against the Java GC. It's very fast for rapid allocation of objects, possibly even getting close to rivaling stack allocation of objects in languages like C++ and very-possibly even beating naive attempts to call
operator new or
malloc per object in such languages without an efficient memory pool involved (fixed allocator, sequential allocator, etc).
The second phase is a bit more complex. After a single GC cycle of this kind of rapid, sequentially-allocated memory, the GC moves (copies) the memory contents of objects still referenced to a more persistent region of memory. This part is a bit of a performance gotcha, since performance-critical fields often need to focus on concepts like spatial locality which ends up becoming lost if the memory contents of objects allocated together are then shuffled and dispersed in memory by a garbage collection cycle.
What does the above imply as far as object pools go? If the idea of using an object pool revolves around:
- Trying to allocate rapid bursts of objects faster, then it's very likely to actually contradict those goals and do more harm than good.
- Trying to improve spatial locality, then it's next to useless since the garbage collector will still rearrange the memory contents after an initial GC cycle for objects that survive (are still referenced) after one collection cycle.
- Trying to improve temporal locality in subsequent accesses, maybe it has some dim chance of improving read/write performance in critical loops for more complex objects.
- Trying to improve deterministic response/latency at the cost of throughput, then it might have some glimmer of a chance (ex: avoiding jarring stutters in frame rates in a video game but with the acknowledgement that the overall frame rate might get slower as a result of the pool even if the frame rate becomes more consistent/stable).
- Trying to reuse higher-level resource concepts and not the idea of the barebones object's memory (covered later), then it might have a good chance of helping.
I'd distinguish "resource pools" from "object pools". For example, threads are very expensive to create in many operating systems (at the native level). A lot of libraries which distribute tasks repeatedly for various threads to process often use thread pools to avoid repeatedly paying the cost of creating and destroying threads.
While probably most Java developers wouldn't use a native thread API for this sort of thinking, imagine a scenario where they do. In that case, perhaps a thread is still modeled as an object.
Here using a thread pool is likely to help (which is pooling objects, but not for the typical reason of naively trying to avoid GC overhead). The pool here is not serving to avoid barebones object allocation/deallocation costs, but instead to avoid repeatedly initializing and destroying threads through the operating system.
In these cases where the resources associated to an object are very expensive to acquire (create) and release (destroy), then "object pools" might help a lot. Yet I wouldn't call them "object pools" anymore, since they're trying to resume using existing resources/states instead of trying to acquire a fresh object.
I often find myself citing data-oriented design as a solution to many performance-critical problems people encounter, but often I see people using object pools in areas like Java games when they will make their life a whole lot simpler and also get genuine, hard improvements in performance by applying data-oriented design instead.
Take this example:
private float size;
private float x;
private float y;
private float z;
Someone might be tempted to pool this kind of
Particle object as particles are born and die every few frames of a video game. An object pool is very unlikely to help here while making the code really dirty and harder to maintain.
Yet the real problem here is that the object-oriented design is being applied at too granular of a level. This translates to definite costs. For example, the size of a particle becomes 24 bytes instead of 16 in such a scenario, leading to more cache misses even in the lucky chance that multiple particles are right next to each other in memory and accessed prior to eviction. Yet any guarantees about spatial locality is effectively lost (we have no guarantees whatsoever that multiple particles will ever be anywhere close to each other in memory).
Instead by doing this:
private float xyzs = new float[n*4];
... we now have a contiguous AoS-style representation which has guaranteed spatial locality from one particle to the next, and no complex dynamics with temporal locality after an initial collection cycle. The size of each particle (at least ones that are accessed -- we might allocate more than necessary using chained pools of floating-point arrays) likewise shrinks down to 16 bytes, and more relevant particles then end up fitting into a 64-byte cache line (4 particles at once). An alternative to allow particles to be added and removed without an anticipated size in advance can be like this:
// How we represent things here doesn't matter so much. More
// important is that we are designing our interfaces in a way
// that leaves us room to change the underlying representation of
// a collection of particles all we like.
// has room for 128 particles.
float xyzs = new float[128*4];
// indicates whether a particle is used (can be tighter
// using bits).
boolean occupied = new boolean;
// reference to next pool (initially null).
// Can use an array list with modulo 128 instead of singly-linked
// unrolled-style list if random-access is desired, can keep a list
// of free indices, etc. etc. etc. Most importantly, we can play with
// this underlying representation all we like without breaking dependencies
// to a ParticleSystem's public interface.
private ParticlePool head = new ParticlePool;
This is not an object pool per se, but a "data" pool of plain old data represented contiguously through an array. It's almost certainly going to help if there's a heavy load here (ex: millions of particles being repeatedly accessed sequentially in critical loops). It's one possible strategy to optimize when dealing with a heavy load without reaching around the objects and trying to pool them individually.
If performance is a goal in cases where the barebones-style object pooling (not resource pooling) is a temptation, I'd suggest applying a data-oriented mindset. Design bulkier interfaces and objects, and you won't be trapped in a memory bottleneck interface design corner since your interfaces will give you a all the breathing room you need to iterate towards more efficient solutions like this.
Another example is to design
Image interfaces instead of
Pixel interfaces. If you have a million simple
People entities you access in loops, design a
People collection interface instead of a
Person, e.g. This kind of data-oriented design approach will leave the breathing room you need to do all the optimization you need at the implementation level without breaking your designs, and without trying to pool little teeny objects.