JDK-7185456 : (ann) Optimize Annotation handling in java/sun.reflect.* code for small number of annotationsC
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Affected Version: 7-pool
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2012-07-20
  • Updated: 2014-10-15
  • Resolved: 2013-03-27
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6 JDK 7 JDK 8
5.0u61Fixed 6u81Fixed 7u60Fixed 8 b86Fixed
Analysis of heap dumps by the JOverflow tool suggests that in business applications, in particular Oracle Fusion Apps, there may be up to 100,000..150,000 instances of the following classes. See this excerpt from JOverflow report for a heap dump for CRMDomain-MarketingServer application:

Object histogram for top memory consumers
 #instances    Shallow size   Impl-inclusive size   Class name
    84,223    11,843K (1.0%)     11,843K (1.0%)    java.lang.reflect.Method
    31,900     3,738K (0.3%)      3,738K (0.3%)    java.lang.reflect.Field
    29,987     3,279K (0.3%)      3,279K (0.3%)    java.lang.reflect.Constructor
    47,526     1,485K (0.1%)      1,485K (0.1%)    sun.reflect.annotation.AnnotationInvocationHandler

These classes store information about annotations for the corresponding members in LinkedHashMaps. There are typically just a few annotations for a given member. However, for a LinkedHashMap the default size of the 'buckets' array is 16, and it looks like these maps are allocated with the default size in the relevant JDK code. As a result, a noticeable amount of memory gets wasted due to the null pointers in 'bucket' arrays of LinkedHashMaps. Here is what JOverflow reports:

  sun.reflect.annotation.AnnotationInvocationHandler.memberValues -->
10,438K (0.9%): LinkedHashMap: 31207 of SMALL 7,615K (0.6%), 46052 of SPARSE_SMALL 2,342K (0.2%), 1084 of BOXED 255K (0.0%), 1440 of EMPTY 225K (0.0%)

  java.lang.reflect.Field.declaredAnnotations -->
4,300K (0.4%): LinkedHashMap: 17380 of SMALL 3,296K (0.3%), 17380 of SPARSE_SMALL 1,003K (0.1%)

  java.lang.reflect.Method.declaredAnnotations -->
2,031K (0.2%): LinkedHashMap: 6791 of SMALL 1,605K (0.1%), 8191 of SPARSE_SMALL 426K (0.0%)

The amount of memory being wasted under SPARSE_SMALL category (which means that less than half of the default 16 slots are used in the map) can be very easily reduced if the respective LinkedHashMaps are allocated with non-default initial size of say 4.

Even more memory (see the SMALL category, which means that the respective map contains 4 elements or fewer) can be saved if LinkedHashMaps are replaced with small ArrayLists or just arrays. Since in most cases the number of annotations is small, simple sequential lookup in lists will likely result in no noticeable performance difference compared to hash map lookup.

Review thread: http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-March/015442.html

EVALUATION Worth exploring.