JDK-8005232 : (JEP-149) Class Instance size reduction
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 8
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2012-12-19
  • Updated: 2014-10-15
  • Resolved: 2013-01-14
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 b75Fixed
Description
JEP-149 looks at dynamic memory footprint reductions in Java 8.

http://openjdk.java.net/jeps/149

This CR covers the "Class Instance Size Reduction" project of that JEP.

In Java 8, using a 32-bit example, a java.lang.Class instance is 112 bytes consisting of:

-  8 byte object header
-  20 declared fields (mostly references, some int)
-  5 injected fields (3 references, 2 ints)

That gives: 8 + (20*4) +(5*4) = 108 bytes. But as we need 8-byte alignment that increases to 112 bytes.

Nine of the reference fields are to SoftReferences (null if not used) that hold cached reflection data (declared methods, fields, constructors etc), all of which must be cleared upon class redefinition. If reflection is not used then these fields are wasting space. There are a number of fields related to annotations, and again if unused these are wasting space - however as JSR-308 is doing a lot of work on annotation types it was decided to leave the annotation related fields alone. There are two further fields (cachedConstructor and newInstanceCallerCache) that pertain to use of the newInstance() method. The very existence of these fields suggests that newInstance is a more common reflection construct to support. Further these are direct references not soft-references and they are not cleared upon class redefinition (arguably a bug), and so for those reasons we also exclude those fields from the present changes.

The original proposal simply moved all the reflection caching soft-references into a separate helper object. That proposal was sent for review here:

http://mail.openjdk.java.net/pipermail/core-libs-dev/2012-April/009749.html

and it was noted by Brian Goetz that additional savings could be made if we held one soft-reference to the helper object rather than a direct reference to a helper containing soft-references.

Peter Levart stumbled into this when he was devising solutions to an annotations processing synchronization bottleneck reported by Alexander Knoller:

http://mail.openjdk.java.net/pipermail/core-libs-dev/2012-November/012049.html

After narrowing the scope to the reflection objects the discussion continued here:

http://mail.openjdk.java.net/pipermail/core-libs-dev/2012-December/012911.html

With Peter's final proposed patch here:

http://dl.dropbox.com/u/101777488/jdk8-tl/JEP-149.c/webrev.04/index.html

In this design we only keep a soft-reference to a ReflectionData object which in turn holds the original fields (but directly) for the cached reflection objects. The ReflectionData object is cleared upon class redefinition and each ReflectionData instance tracks the class redefinition count at the time it was created. This in turn required a seperate redefinition count to be maintained for the annotations processing. As a result we have the following layout changes:

-  8 reference fields moved (the reflection caches)
-  1 int moved (the class redefinition count)
-  1 reference added (for the SoftReference to the ReflectionData)
-  1 int added (the class redefinition count for annotations)

This is a saving of 7 reference fields ie. 28 bytes, resulting in a new Class instance size of 80 bytes. This saves a further 4 bytes due to the fields being 8-byte aligned without any need for padding. So overall we save 32 bytes per class instance.

The ReflectionData instance itself consumes 48 bytes, while a SoftReference consumes 32 bytes.

For classes that don't use reflection this is an obvious win of 32 bytes per class. For classes that use all 8 reflection caches this is also a win as we save 7 SoftReferences ie  224 bytes.

For classes that only use one cached reflection object, however, there is a space penalty. The existing layout would consume 112 bytes for the Class instance, plus 32 bytes for the SoftReference to hold the cached array. A total of 144 bytes. The new layout consumes 80 bytes for the Class instance, 32 bytes for the SoftReference to the ReflectionData, and 48 bytes for the ReflectionData instance: a total of 160 bytes. Hence we have a 16 byte space penalty if only one reflection cache is needed. Note that if two reflection caches are used then we are again in front as the new scheme requires no further allocations, where the old would add a second SoftReference at 32-bytes, thus giving the new scheme a 16 byte advantage.
Comments
I did some basic instrumentation and ran the refworkload benchmark suite to see what percentage of classes resulted in inflating their ReflectionHelper, but did not track the amount of reflection data actually used. Typically less than 3% of classes used reflection; but it was as high as 14-15% for some apps. But this was sufficient to indicate that all of the apps would get a net reduction in memory use from the use of the ReflectionHelper. Annotations were raised in the discussions (linked above) but at the time there was a lot of additional work going on around field and method annotations, so we wanted to avoid any encroachment on that work, and to give ourselves a well-defined boundary to work to in the time constraints that we had. There is certainly further scope for moving out seldomly used fields into additional helper objects. As always it comes down to the statistics: how often do we win, how often do we lose - and what impact does this have on throughput performance.
26-04-2013

As noted in JDK-8013267, I want to add another pointer to java.lang.Class, to move what is currently stored in Serguei's MemberNameTable into the Java heap. http://cr.openjdk.java.net/~sspitsyn/webrevs/2013/hotspot/8008511-JVMTI-JSR292.1/src/share/vm/prims/methodHandles.cpp.cdiff.html It would be wrong to put this stuff under a SoftReference, so I need a different kind of extension record. But I would like to build on (and perhaps improve) the work described under this bug. To start with, I have a question: Are there statistics available on how often the various Class fields are null? I notice that ReflectionData does not contain some reflection data, such as class annotations. I suppose this was because the work for 8005232 had to draw a line around all the things under soft references. Still, you could add fields to the SoftReference object and hoist the class annotations there. Or was there another problem? It seems to me that (depending on statistics), it would be good to make an ClassData struct which would *also* be an optional extension record, which would hold all mostly-null variables from Class. I would move the native MemberNameTable stuff into ClassData, and also put a link from ClassData to the ReflectionData created by 8005232. Finally, as a hack to save space, the ClassData would extend (not link to) the current SoftReference, so that existing code to grab ReflectionData would remain essentially unchanged. The net result would be a footprint reduction, I think, even without the final hack. Comments?
25-04-2013

Marked as noreg-cleanup. This is a refactoring of existing code that is covered by existing tests.
16-04-2013

Performance results: ============================================================================== /space/Embedded-Libs/perfmeasures/peter-levart-getannotations/v3/results/20121218-1230.footprint3_real.I10.ref.4951: Benchmark Samples Mean Stdev Geomean Weight footprint3_real 10 88411.55 393.98 Framer 10 45415.20 13.31 0.03 JEdit 10 66316.80 12.62 0.30 LimeWire 10 85094.40 15.91 0.30 NetBeans 10 155795.20 2315.59 0.30 Noop 10 37557.60 11.96 0.03 XFramer 10 45544.80 13.31 0.04 ============================================================================== /space/Embedded-Libs/perfmeasures/peter-levart-getannotations/v3/results/20121218-1230.footprint3_real.I10.test.4951: Benchmark Samples Mean Stdev %Diff P Significant footprint3_real 10 88212.52 685.21 0.23 0.439 * Framer 10 45489.60 11.81 -0.16 0.000 Yes JEdit 10 66430.40 5.06 -0.17 0.000 Yes LimeWire 10 85212.80 18.93 -0.14 0.000 Yes NetBeans 10 154089.60 3985.38 1.09 0.261 * Noop 10 37635.20 14.21 -0.21 0.000 Yes XFramer 10 45601.60 16.35 -0.12 0.000 Yes ============================================================================== /space/Embedded-Libs/perfmeasures/peter-levart-getannotations/v3/results/20121218-1100.reference_client.I10.ref.18056: Benchmark Samples Mean Stdev Geomean Weight reference_client 10 801.65 3.15 footprint3_real 10 88502.35 540.30 0.20 jetstream_client 10 87.53 1.75 0.14 specjvm98_client 10 141.06 1.75 0.10 startup3 10 3.14 0.03 0.08 swingmark 10 677.35 5.27 0.08 ============================================================================== /space/Embedded-Libs/perfmeasures/peter-levart-getannotations/v3/results/20121218-1100.reference_client.I10.test.18056: Benchmark Samples Mean Stdev %Diff P Significant reference_client 10 801.15 4.47 -0.06 0.776 * footprint3_real 10 87825.96 1020.35 0.76 0.086 * jetstream_client 10 87.50 1.94 -0.03 0.973 * specjvm98_client 10 141.18 1.37 0.09 0.866 * startup3 10 3.19 0.05 -1.61 0.013 * swingmark 10 676.12 4.97 -0.18 0.598 * ============================================================================== /space/Embedded-Libs/perfmeasures/peter-levart-getannotations/v3/20121217-2300.reference_server.I10.ref.16448: Benchmark Samples Mean Stdev Geomean Weight reference_server 10 7657.60 29.78 jetstream 10 86.69 1.27 0.10 scimark 10 139.49 1.15 0.15 specjbb2000 10 191937.18 919.00 0.15 specjbb2005 10 84366.25 401.21 0.25 specjvm98 10 227.57 0.67 0.15 volano25 10 90198.40 876.97 0.20 ============================================================================== /space/Embedded-Libs/perfmeasures/peter-levart-getannotations/v3/20121217-2300.reference_server.I10.test.16448: Benchmark Samples Mean Stdev %Diff P Significant reference_server 10 7654.40 28.76 -0.04 0.809 * jetstream 10 86.89 1.72 0.24 0.767 * scimark 10 139.47 0.74 -0.02 0.955 * specjbb2000 10 192913.93 1258.50 0.51 0.064 * specjbb2005 10 84180.00 333.43 -0.22 0.274 * specjvm98 10 227.89 0.89 0.14 0.369 * volano25 10 89727.60 657.12 -0.52 0.192 * There are no glaring performance issues.
07-01-2013