JDK-8210926 : vmTestbase/nsk/jvmti/scenarios/allocation/AP11/ap11t001/TestDescription.java failed with JVMTI_ERROR_INVALID_CLASS in CDS mode
  • Type: Bug
  • Component: hotspot
  • Sub-Component: jvmti
  • Affected Version: 11,12
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2018-09-19
  • Updated: 2019-08-15
  • Resolved: 2018-10-01
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 11 JDK 12
11.0.10-oracleFixed 12 b14Fixed
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Description
The test failed with the following error when running with the default CDS archive in tier6:

The following fake exception stacktrace is for failuire analysis. 
nsk.share.Fake_Exception_for_RULE_Creation: (ap11t001.cpp:47) NSK_CPP_STUB4(GetClassSignature, jvmti, classes[i], &signature, &generic)
	at nsk_lvcomplain(nsk_tools.cpp:172)
# ERROR: ap11t001.cpp, 47: NSK_CPP_STUB4(GetClassSignature, jvmti, classes[i], &signature, &generic)
#   jvmti error: code=21, name=JVMTI_ERROR_INVALID_CLASS
# ERROR: ap11t001.cpp, 145: VMObjectAlloc: object_klass is not found in the list of loaded classes: Ljava/lang/invoke/MemberName;
# ERROR: ap11t001.cpp, 47: NSK_CPP_STUB4(GetClassSignature, jvmti, classes[i], &signature, &generic)
#   jvmti error: code=21, name=JVMTI_ERROR_INVALID_CLASS
# ERROR: ap11t001.cpp, 145: VMObjectAlloc: object_klass is not found in the list of loaded classes: Ljava/lang/String;
# ERROR: ap11t001.cpp, 47: NSK_CPP_STUB4(GetClassSignature, jvmti, classes[i], &signature, &generic)
#   jvmti error: code=21, name=JVMTI_ERROR_INVALID_CLASS
# ERROR: ap11t001.cpp, 145: VMObjectAlloc: object_klass is not found in the list of loaded classes: [B
Comments
Fix Request This patch resolves a CDS and JVMTI interaction issue that affects 11u users. Patch applies cleanly to 11u.
18-06-2019

I was able to reproduce the failure using com/sun/jdi/ClassesByName2Test.java. This turns out to be a CDS specific bug. When we archive the classes at dump time, we reset the InstanceKlass::_init_state back to 'loaded' state before writing out the data. Therefore, at runtime during loading of a shared class, there is a 'brief' moment JvmtiEnv::GetLoadedClasses() in another thread could see a 'loaded' shared class without mirror. NULL mirror is not the only issue, other fields of the shared InstanceKlass may not yet setup properly. To fix the issue, we need to reset to _init_state to 'allocated state before writing out the archived classes at dump time.
28-09-2018

Also see JDK-8210926, which appears to be a duplicate that causes com/sun/jdi/ClassesByName2Test.java to fail.
26-09-2018

If I'm reading the analysis correctly you would expect to see !loaded if the mirror is NULL, so the problem can't be a missing release_store. Is the loaded state initialized properly?
22-09-2018

Hmmm, ClassLoaderData::loaded_classes_do() only takes a class if it is 'loaded'. void ClassLoaderData::loaded_classes_do(KlassClosure* klass_closure) { // Lock-free access requires load_acquire for (Klass* k = OrderAccess::load_acquire(&_klasses); k != NULL; k = k->next_link()) { // Do not filter ArrayKlass oops here... if (k->is_array_klass() || (k->is_instance_klass() && InstanceKlass::cast(k)->is_loaded())) { klass_closure->do_klass(k); } } } A class is set to 'loaded' state in SystemDictionary::add_to_hierarchy(), which happens after mirror creation. Could it be possibly a MP issue and do we need a OrderAccess::release_store for set_init_state(InstanceKlass::loaded)?
21-09-2018

Still have no luck reproducing the failure. However this seems to be a possible scenario: In the callback, JvmtiGetLoadedClasses::getLoadedClasses() (called from JvmtiEnv::GetLoadedClasses()) acquires following two locks before calling into ClassLoaderDataGraph::loaded_classes_do(&closure): * MultiArray_lock * ClassLoaderDataGraph_lock The callback can happen in the 'main' and 'MainThread' threads in the test. During class loading, a klass is added to ClassLoaderData before mirror is created (or restored if the class is shared). ClassLoaderDataGraph_lock is not locked when calling ClassLoaderData::add_class(). It is possible, JvmtiEnv::GetLoadedClasses() could encounter a klass (is being loaded in other threads) whose mirror is not yet created in rare cases. Then a null object could be added to the collected classes array. That could cause the failure observed in the bug and the NullPointerException in JDK-8209736. LoadedClassesClosure (prims/jvmtiGetLoadedClasses.cpp): void do_klass(Klass* k) { // Collect all jclasses _classStack.push((jclass) _env->jni_reference(Handle(_cur_thread, k->java_mirror()))); if (_dictionary_walk) { // Collect array classes this way when walking the dictionary (because array classes are // not in the dictionary). for (Klass* l = k->array_klass_or_null(); l != NULL; l = l->array_klass_or_null()) { _classStack.push((jclass) _env->jni_reference(Handle(_cur_thread, l->java_mirror()))); } } }
21-09-2018

I added some debugging output in the VM and test and fired 5 tier6 runs last night. The test executes 19 to 25 times (including all platforms) in every tier6 testing. However, the failure was not reproduced. Still no luck with reproducing it after many runs today.
21-09-2018

Kim pointed out there are two other possible cases in jvmti_GetClassSignature() (in generated gensrc/jvmtifiles/jvmtiEnter.cpp) before GetClassSignature(): if (k_mirror == NULL) { return JVMTI_ERROR_INVALID_CLASS; } if (!k_mirror->is_a(SystemDictionary::Class_klass())) { return JVMTI_ERROR_INVALID_CLASS; } Thanks, [~kbarrett]! I wonder if this could be the same issue as JDK-8209736, where NullPointerException is thrown when iterating through the classes array returned by getAllLoadedClasses().
20-09-2018

# ERROR: ap11t001.cpp, 47: NSK_CPP_STUB4(GetClassSignature, jvmti, classes[i], &signature, &generic) From above error, looks like the failure happens in the NULL_CHECK in JvmtiEnv::GetClassSignature: jvmtiError JvmtiEnv::GetClassSignature(oop k_mirror, char** signature_ptr, char** generic_ptr) { ResourceMark rm; bool isPrimitive = java_lang_Class::is_primitive(k_mirror); Klass* k = NULL; if (!isPrimitive) { k = java_lang_Class::as_Klass(k_mirror); NULL_CHECK(k, JVMTI_ERROR_INVALID_CLASS); } java_lang_Class::is_primitive() is called before java_lang_Class::as_Klass() in JvmtiEnv::GetClassSignature(). java_lang_Class::is_primitive() is false, which means the java_class->metadata_field(_klass_offset) is not NULL at the time. Somehow, by the time java_lang_Class::as_Klass() is called, java_class->metadata_field(_klass_offset) becomes NULL. bool java_lang_Class::is_primitive(oop java_class) { // should assert: //assert(java_lang_Class::is_instance(java_class), "must be a Class object"); bool is_primitive = (java_class->metadata_field(_klass_offset) == NULL); #ifdef ASSERT if (is_primitive) { Klass* k = ((Klass*)java_class->metadata_field(_array_klass_offset)); assert(k == NULL || is_java_primitive(ArrayKlass::cast(k)->element_type()), "Should be either the T_VOID primitive or a java primitive"); } #endif Klass* java_lang_Class::as_Klass(oop java_class) { //%note memory_2 assert(java_lang_Class::is_instance(java_class), "must be a Class object"); Klass* k = ((Klass*)java_class->metadata_field(_klass_offset)); assert(k == NULL || k->is_klass(), "type check"); return k; } return is_primitive; } There is no class unloading in this case, I'm puzzled why java_class->metadata_field(_klass_offset) becomes NULL in the java_lang_Class::as_Klass() call.
20-09-2018