JDK-7177409 : Perf regression in JVM_GetClassDeclaredFields after generic signature changes
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: hs24
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2012-06-15
  • Updated: 2013-07-18
  • Resolved: 2012-07-03
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.
JDK 7 JDK 8 Other
7u40Fixed 8Fixed hs24Fixed
When analyzing a timeout on gc/gctests/LargeObject/large00x I used PrintSafepointStatistics to see if there were any Safepoint issues causing the timeout and saw some pretty bad numbers:
> vmop [threads: total initially_running wait_to_block] [time: spin block 
> sync cleanup vmop] page_trap_count
> 5.693: EnableBiasedLocking [ 19 0 1 ] [ 0 230493 230493 0 0 ] 0
> (...)
> vmop [threads: total initially_running wait_to_block] [time: spin block 
> sync cleanup vmop] page_trap_count
> 435.543: RevokeBias [ 19 0 1 ] [ 0 126420 126421 0 0 ] 0

This led me to run some measurements with the linux "perf" tool, results attached. As you can see, with the 7168280 changes we spend a lot of time in fieldDescriptor::generic_signature().

The changes to make generic signature index slot optional seems to have caused a performance regression when getting all fields through reflection on large classes.
This caused the gc/gctests/LargeObject tests to time out on slower hardware.

The issue seems to be that given a class with thousands (65k) of fields a call to JVM_GetClassDeclaredFields will in the worst case (which seems to be triggered here) make the VM go through all the fields of the class for each call to Reflection::new_field(&fd, UseNewReflection, CHECK_NULL);
because of the loop in fieldDescriptor::generic_signature()

This is basically making the time complexity of JVM_GetClassDeclaredFields O(n^2) where n is the number of fields in the class. This seems to have caused the safepoint stalls seen in the test (and the slow execution of the test).

EVALUATION http://hg.openjdk.java.net/lambda/lambda/hotspot/rev/634b8615a6ba

EVALUATION http://hg.openjdk.java.net/hsx/hotspot-emb/hotspot/rev/634b8615a6ba

EVALUATION Thanks for the detailed analysis and suggested fix! The suggested fix looks correct. We could do the access_flags().field_has_generic_signature() check in fieldDescriptor::generic_signature() before looping through fields and returns NULL immediately if the field has no generic signature. That way the caller of fieldDescriptor::generic_signature() doesn't need to do any special handling.

SUGGESTED FIX I tried a simple fix to only call fd->generic_signature() if the field has the flag set and this seems to have restored the performance back to normal but I'm not sure if it's semantically correct. diff -r 063451aefde8 src/share/vm/runtime/reflection.cpp --- a/src/share/vm/runtime/reflection.cpp +++ b/src/share/vm/runtime/reflection.cpp @@ -829,7 +829,7 @@ java_lang_reflect_Field::set_modifiers(rh(), fd->access_flags().as_int() & JVM_RECOGNIZED_FIELD_MODIFIERS); java_lang_reflect_Field::set_override(rh(), false); if (java_lang_reflect_Field::has_signature_field() && - fd->generic_signature() != NULL) { + fd->access_flags().field_has_generic_signature()) { Symbol* gs = fd->generic_signature(); Handle sig = java_lang_String::create_from_symbol(gs, CHECK_NULL); java_lang_reflect_Field::set_signature(rh(), sig());