How Slow is Reflection in Android?
- English |
- Русский |
(So far we’ve analyzed a lot of apps and discovered a handful of issues that significantly slow down many apps. Starting from this post, we’ll describe these issues one by one.)
Reflection is, of course, an extremely useful aspect of Java and Android development. Yet it turns out that reflection can very often be the source of significant slowdown within an Android application. Perhaps the most intuitive way of understanding this is going through a couple of real-life examples.
Two Real-world Examples
Our first example involves NYTimes Android app. With the help of NimbleDroid, our friends at NYTimes found out that the reflective type adapters in Gson cost their Android app a 700ms startup delay. They eventually fixed this delay with manually written custom type adapters.
Our second example involves Photobucket, a large photo-sharing platform. Here, reflection again causes a big bottleneck.
We see that the
com.photobucket.api.client.jersey.UserClient constructor takes an entire 660ms to run. Looking further into the icicle graph, we see that the reason for such a lag lies in reflection. Check this out:
Note that the getGenericInterfaces() method returns the types of the interfaces that this class directly implements. Here, it is invoked 5 times and takes ~81ms. Sure, on the surface this may not seem like much, but altogether, use of the method is causing ~600ms of start time delay. Let’s take a deeper look at why this is taking so long.
It turns out that this library allows developers to configure a REST client with annotations. The issue is that the library doesn’t process the annotations during build time, but instead parses and creates the REST client during runtime (with the help of reflection). From a performance point of view, this is catastrophic.
We’ve created a simple test to quantify how slow reflection is.
We will work with the android.app.Activity class and repeat operations 10,000 times, like this:
We’ve also set up two tests that include creating objects (of the type DummyItem, an empty dummy class) to look at the overhead purely caused by reflection. Here’s an example:
And here are our results (all numbers are in ms, measured on our personal devices with real usage to make the results more faithful to real world):
|NEXUS 5 (6.0) ART||GALAXY S5 (5.0) ART||GALAXY S3 mini (4.1.2) Dalvik|
It’s evident that reflection in Android is excruciatingly slow - compare the (1332ms, 6384ms, 2891ms) with reflection to the (312ms, 358ms, 774ms) without reflection. Interestingly, Android 5.0 ART on a more powerful device actually makes reflection much slower than Android 4.1 Dalvik on a less powerful device; only in Android 6.0 ART is the overhead reduced, but the overhead is still quite significant.
More real-world examples
ActiveAndroid is another library that uses reflection. Let’s take a look at how it can affect start time by analyzing some real apps on the Play store:
Here’s the Scribd app:
Myntra sees similar problems:
As you can see, the library requires more than a second to initialize. That’s a lot of time, especially taking into consideration that users expect an average app start time of 2s.
To conclude, reflection in Android is really slow. To guarantee you offer users the smoothest experience possible, we recommend the following:
Recommendation: understand what you're getting into when using reflection (or libraries that use reflection). In particular, do not use reflective type adapters to serialize or deserialize Java objects.
(Mar 3, 2016: we updated our recommendation thanks to the discussion with Jake Wharton.)