I recently profiled an app of mine. During profiling, I’ve noticed tons of array allocations. Something like:
Well easy, a quick grep for TypeInfo array. Nothing. Ah, quick grep for TypeInfo collections. Nothing! Huh? Where the hell do I allocate the array then?
Ok, more profiling! So, I enabled flight recorder with ‘-XX:+UnlockCommercialFeatures -XX:+FlightRecorder’. Then did a recording with Java Mission Control, capturing the allocation stack traces:
Ok, the TypeInfo.values() method allocates the memory. TypeInfo is a enum, and I’m using the built in values() method. The code looked something like this:
Do you see the allocation? Why should it allocate that many arrays? The Enum values are constant, so it should be able to initialize an array once?
Ok, let’s take a closer look and decompile the class file:
javap -c TypeInfo.class | grep -A 10 values
And we get:
public static info.gamlor.blog.TypeInfo values(); Code: 0: getstatic #1 // Field $VALUES:[Linfo/gamlor/blog/TypeInfo; 3: invokevirtual #2 // Method "[Linfo/gamlor/blog/TypeInfo;".clone:()Ljava/lang/Object; 6: checkcast #3 // class "[Linfo/gamlor/blog/TypeInfo;" 9: areturn
As we can see, it deferences a static array of the Enum values. Then it clones that array before returning it, causing an array allocation. Why does it do that? It is a defensive copy:
Java’s arrays are a mutable. If values() just returns the reference to the original array, then some code might change the array’s content. That creates havoc, since it would change what Enum.values() returns. Therefore, the defensive copy.
So, after seeing this, it is easy to fix our code. We call Enum.values() once, cache the array and use it for the parsing:
Et voila, the allocations are gone =)
- Deploying JVM in tiny containers, be carefull!
- Adding Timestamp to Logs