Increasing the performance of your Android apps is sometimes an endless goal. Squeezing a few more FPS out of an animation loop can make or break a user experience. Stutter in audio wrecks a music app. In this constant search for optimizations, many developers are troubled by the number of Objects that can be created in a Java application. Is this really a big issue? What can be done about it? Read on to learn whether or not Object Pools make sense for your application. You may find a few more FPS hidden in the heap.
Garbage vs. Recycling
One of the major ways that developing mobile applications for Android differs from iOS is how memory is managed. As you probably know, the Android Dalvik Virtual Machine (DVM) nicely manages memory for you, like the Java JVM does.
Unlike iOS, the Android DVM has true Garbage Collection (GC). You ask for an object with ‘new’, the DVM gives one to you, and then it cleans the object up when you aren’t using it anymore.
The process that the DVM uses to reclaim unused objects is called the GC cycle. We aren’t going to go into the details of how the GC works in Android. For the purposes of this article, it’s sufficient to say that the GC is a black box out of our control. The CPU and memory demands of the GC system can, in some circumstances, affect performance of application code.
Is GC performance really a big deal for application developers? This is the source of a lot of debate in the Android (and Java) communities. Some people say the DVM GC is so efficient that trying to “work around” its performance characteristics will just make things worse. Other people suggest that doing anything which prevents object creation can only have a positive impact.
If you decide that GC performance really is negatively impacting your application, one solution is to use a design pattern called Object Pools. These structures are also sometimes called Object Recyclers, because instances are reused many times by an application.
What are Object Pools?
Object Pools pretty much do what they say on the tin. They maintain an internal pool of objects. They also maintain the state of these objects – whether or not they are being used by the application.
In essence, Object Pools make the classic space-time tradeoff. Object Pools are just special Facades over the DVM memory management. The objects in the pool are pre-allocated when the pool structure is created. While expensive, this only needs to be done once.
When the application wants to use a poolable object, it requests one from the Object Pool instead of allocating a new one. When the application is finished with the object, it is explicitly released back to the Object Pool. This releasing operation is a big difference from traditional GC, and is a critical requirement of using an Object Pool. If the application fails to release objects back to the Object Pool, all of the pooled objects will become ‘used’ and the benefits of the pool are negated.
Object Pools are almost always implemented with a fixed object count. If the application requests an object from the Object Pool but none are available, the Object Pool falls back to allocating an object via ‘new’. It is possible to design an Object Pool which can grow dynamically, but this quickly diminishes the benefits of the pattern, which is constant and predictable execution time.
Thread safety is an important implementation consideration when building an Object Pool. It must be assumed that allocation and release of objects can (and will) happen concurrently. At the same time, aggressive synchronization can cause lock contention which reduces performance. Mindful use of the thread safe classes in java.util.concurrent can go a long way towards building a safe and fast Object Pool.
Another consideration when using Object Pools is that an object may be ‘dirty’ when it is given to an application. That is, it may contain state from its last usage. This is particularly true if the Object Pool does not force an object to conform to a particular interface or contract. For example, some Object Pool designs may require pooled objects to implement a kind of ‘Poolable’ interface. If the interface requires a ‘clear’ method, this method could be automatically called upon allocation or release. This would prevent the application from receiving ‘dirty’ objects. If the pool functions with generic objects, no guarantees can be made as to the methods that the object class implements. In these cases, it is up to the application to clear the object state.
Whether or not Object Pools are Considered Harmful is the source of a bit of debate. Typically, the debate follows this general pattern:
Bob: Java creates too many objects.
Alice: Well, that’s Java. Creating new instances is really efficient these days.
Bob: All this object creation is causing a lot of GC and heap thrashing.
Alice: Is that really impacting your application?
Bob: Yes, because my application has extreme constraints with (animations|synthesis|number crunching)
Alice: Maybe you could use an object pool.
There is really no right answer as to whether or not an Object Pool is a good design pattern. It’s basically just another type of cache, albeit operating differently than regular caches.
Generally, Object Pools can increase performance if many short lived objects are being created and destroyed in a tight, resource constrained loop. This can commonly occur when animating graphics, generating synthesized sound, or crunching scientific computations.
Outside of this narrow case, the benefits of Object Pools are more difficult to justify. Dalvik (and the JVM) do a very good job at managing memory, and it’s hard to improve upon.
Pros and Cons
Out of this debate let’s distill some pros and cons about using Object Pools:
- It does reduce heap thrash, since some objects are not being rapidly allocated and freed.
- This in turn makes the GC’s execution profile more predictable and “flat”.
- Having a more predictable GC reduces any impact that GC may have on application code.
- There are real use cases for real-time and resource intensive applications.
- It’s hard to manage memory better than the DVM, and you could make things worse.
- You could introduce concurrency bugs or performance bottlenecks.
- Bugs could be caused by dirty objects – old state hanging around.
- Object Pools that require objects to conform to interfaces can cause design creep in your solution.
- Memory allocation is rarely the source of performance issues.
Make a data driven decision to use Object Pools
Before you decide to use an Object Pool, do some science. Collect data about how fast your code is executing. Introduce an Object Pool and measure it again. Was there a performance improvement? If so, that’s excellent. If not, the architectural overhead of an Object Pool may not be worth it.
In the case where Object Pools do significantly increase your application’s performance, be sure to unit test it heavily. Write highly concurrent “torture” tests.
Share Your Thoughts!
Do you use Object Pools? Do you have strong opinions about Object Pools? Come share your experiences with us on our Community Forum!