An Object Pool avoids cost of initializing objects by maintaining a pool of pre-initialized objects that can be re-used.
Performance can be an important consideration for applications. In some scenarios, object creation is a costly step.
Avoids cost of initializing objects by maintaining a pool of pre-initialized objects that can be re-used.
Description of Benefits
Can offer a performance boost where:
- object instantiation is expensive
- instances of the class are frequently created
- number of instances at any one time is small
Can make initialization time predictable where it would otherwise be unpredictable (e.g. when acquiring resources over a network)
- Shoe shelf at a bowling club
- Car pooling
- Hire / rental businesses
Instantiation of objects that represent:
- database connections
- socket connections
- large graphic objects
An object used by Client until they it is no longer required.
Uses an instance of Reusable for a limited amount of time
Manages the collection of Reusable objects by:
- creating new instances of Reusable
- supplying instances of Reusable to Clients
Typical methods induce:
- getInstance: A static method that returns an instance of ReusablePool
- aquireReusable:A method that returns an instance of Reusable
- releaseReusable(Reusable):A method that returns a Reusable to the pool
Basic Implementation (Collaboration)
The Client is responsible for requesting the Reusable from the ReusablePool.
On receiving a request for an object, the ReusablePool will attempt to supply an suitable Reusable.
The Client should be unaware that the Reusable can be shared with other Clients: from the Client‘s point of view, the Reusable can be treated in exactly the same way as an object that has been created in any other way,the only difference being that it must return it to the ReusablePool once it has finished using it.
The Client is responsible for returning Reusables to the ReusablePool once it is finished with.
Resource Loading Strategy
Several strategies are available, for example:
- Eager: A specified number of Reusables are created by the ReusablePool when the ReusablePool is instantiated.
- Lazy: Reusables are not created by the ReusablePool until they are requested by the a Client. Once Reusables are released, they are immediately available for other Clients.
- Hybrid: A specified number of Reusables are created eagerly, but additional Reusables are created lazily.
- Lazy-Expanding: Creates resources lazily, but doesn’t re-use them until the ReusablePool reaches a certain size.
- Eager-Expanding: Creates resources eagerly. Creates additional resources when available objects in the ReusablePool drops below a certain threshold.
Maximum Pool Size
Some implementations may set a maximum number of Reusables in the ReusablePool
Empty (Exhausted) Pool
Various strategies can be adopted to handle the situation where a Client requests an instance from the pool, but none is currently available:
- Prevent the situation by ensuring that the ReusablePool will always contain enough Reusables.
- Fail to provide Reusable, and inform the Client that none is available
- Create a new Reusable, thus increasing the size of the pool. For example, a library might request a copy of a book from another library.
- Block until another thread to releases an object back into the pool
- Forcible reclaim a Reusable from a low-priority Client.
- Offer an alternative (rarely seen in programming, but common for real-world examples)
In a multi-threaded environment, careful consideration must be given to synchronisation of methods on the ResourcePool object.
If the Client fails to return a Reusable to the pool, then it will be unavailable to other Clients.
To avoid this, the ResourcePool could implement an expiry time for Reusable. If the Reusable has not actually been used for a certain length of time, it can be made available to other Clients.
The ReusablePool may need to take a Reusable back from clients, for example:
- Because a higher priority client has requested it
- Following a time-out
This can be implemented in a number of ways, for example:
- Request a Client return the Reusable.
- Throw an exception whenever the Client attempts to use the Reusable.
In some situations, it may be undesirable to hold unused Reusables in the ReusablePool for long periods of time. In this case, it may be desirable to evict Reusables from the ReusablePool if they have not been requested by a Client for a specified length of time.
Some types of Reusables may need to be reset to a known state before they can be allocated to another Client. This is normally the responsibility of the ReusablePool: subtle bugs can be introduced if it is left to the Clients.
There are costs associated with managing the pool.
- Can add complexity to code
- Reusables in the pool will tie up resources, e.g. memory
- Module start-up times may be increased when objects are created eagerly
- Mixed pools, which contain more than one kind of Reusable.
Relationship with Other Patterns
- ReusablePool is often implemented as a Singleton.
A similar pattern (the “Stock Room” pattern, for want of a better name) is sometimes used where a store of entities is created eagerly (typically during “quiet periods” when resource is available). They are available for consumption by clients as required, but are not returned to the store once used. A book shop is a real world example, where the store keeps a stock of books available, but unlike a library (a true pool) they aren’t returned to the shop once they’ve been read.