I tend to use object pools in servers and other such apps that are characterized by continual and frequent allocation and release of large numbers of a few sets of objects, (in servers - socket, buffer and buffer-collection classes). The pools are queues, created at startup with an appropriate number of instances pushed on, (eg. my server - 24000 sockets, 48000 collections and an array of 7 pools of buffers of varying size/count). Popping an object instance off a queue and pushing it back on is much quicker than new/delete, even if the pool queue has a lock because it is shared across the threads, (the smaller the lock span, the smaller the chance of contention). My pooled-object class, (from which all the sockets etc. are inherited), has a private 'myPool' member, (loaded at startup), and a 'release()' method with no parameters & so any buffer is easily and correctly returned to its own pool. There are issues:
1) Ctor and dtor are not called upon allocate/release & so allocated objects contain all the gunge left over from their last use. This can occasionally be useful, (eg. re-useable socket objects), but generally means that care needs to be taken over, say, the initial state of booleans, value of int's etc.
2) A pool per thread has the greatest performance improvement potential - no locking required, but in systems where the loading on each thread is intermittent, ths can be an object waste. I never seem to be able to get away with this, mainly because I use pooled objects for inter-thread comms and so release () has to be thread-safe anyway.
3) Elimination of 'false sharing' on shared pools can be awkward - each instance should be initially 'newed' so as to exclusively use up an integer number of cache pages. At least this only has to be done once at startup.
4) If the system is to be resilient upon a pool running out, either more objects need to be allocated to add to the pool when needed, (the pool size is then creeping up), or a producer-consumer queue can be used so that threads block on the pool until objects are released, (P-C queues are slower because of the condvar/semaphore/whatever for waiting threads to block on, also threads that allocate before releasing can deadlock on an empty pool).
5) Monitoring of the pool levels during development is required so that object leakages and double-releases can be detected. Code/data can be added to the objects/pools to detect such errors as they happen but this compromises performance.