CDS and JVM-TI agent questions and crash

Jiangli Zhou jiangli.zhou at oracle.com
Mon Oct 15 19:09:37 UTC 2018


I logged issue in JDK-8212200, 
https://bugs.openjdk.java.net/browse/JDK-8212200.

Thanks,

Jiangli


On 10/15/18 11:50 AM, Jiangli Zhou wrote:
> Hi Volker,
>
> Thanks for taking this! I was thinking about this issue also. We 
> probably should disable sharing by turning off UseSharedSpaces (in 
> KlassFactory::check_shared_class_file_load_hook) if a shared class 
> loaded by boot loader is redefined. Archived sub-classes are 
> invalidated and not used if the super class is different. When shared 
> java.lang.Object is redefined, all archived classes are invalidated.
>
> Michael, please see more comments below.
>
> On 10/15/18 11:07 AM, Volker Simonis wrote:
>> Just a quick analysis (will look into a fix tomorrow).
>>
>> - When saving a class to the CDS archive, the VM calls
>> ConstantPool::remove_unshareable_info() on its constant pool:
>>
>> void ConstantPool::remove_unshareable_info() {
>> ...
>>    // Shared ConstantPools are in the RO region, so the _flags cannot
>> be modified.
>>    // The _on_stack flag is used to prevent ConstantPools from
>> deallocation during
>>    // class redefinition. Since shared ConstantPools cannot be
>> deallocated anyway,
>>    // we always set _on_stack to true to avoid having to change _flags
>> during runtime.
>>    _flags |= (_on_stack | _is_shared);
>>
>> This calls sets the '_on_stack' flag on the constant pool for the
>> reasons described in the comment.
>>
>> - When a class is being loaded from the CDS archive, the VM calls
>> ConstantPool::restore_unshareable_info(TRAPS) on its constant pool:
>>
>> // CDS support. Create a new resolved_references array.
>> void ConstantPool::restore_unshareable_info(TRAPS) {
>>    assert(is_constantPool(), "ensure C++ vtable is restored");
>>    assert(on_stack(), "should always be set for shared constant pools");
>>    assert(is_shared(), "should always be set for shared constant 
>> pools");
>>    assert(_cache != NULL, "constant pool _cache should not be NULL");
>>
>> This calls asserts for the '_on_stack' flag being set.
>>
>> - When loading a class from the CSD archive
>> withSystemDictionary::load_shared_class(),
>> ConstantPool::restore_unshareable_info(TRAPS) is actually called from
>> InstanceKlass::restore_unshareable_info(TRAPS) after the instanceKlass
>> was loaded:
>>
>> InstanceKlass* SystemDictionary::load_shared_class(InstanceKlass* ik,
>>                                                     Handle class_loader,
>>                                                     Handle
>> protection_domain, TRAPS) {
>> ...
>>      InstanceKlass* new_ik = 
>> KlassFactory::check_shared_class_file_load_hook(
>>          ik, class_name, class_loader, protection_domain, CHECK_NULL);
>>      if (new_ik != NULL) {
>>        // The class is changed by CFLH. Return the new class. The 
>> shared class is
>>        // not used.
>>        return new_ik;
>>      }
>>
>>      // Adjust methods to recover missing data.  They need addresses for
>>      // interpreter entry points and their default native method address
>>      // must be reset.
>>
>>      // Updating methods must be done under a lock so multiple
>>      // threads don't update these in parallel
>>      //
>>      // Shared classes are all currently loaded by either the 
>> bootstrap or
>>      // internal parallel class loaders, so this will never cause a 
>> deadlock
>>      // on a custom class loader lock.
>>
>>      ClassLoaderData* loader_data =
>> ClassLoaderData::class_loader_data(class_loader());
>>      {
>>        HandleMark hm(THREAD);
>>        Handle lockObject = compute_loader_lock_object(class_loader, 
>> THREAD);
>>        check_loader_lock_contention(lockObject, THREAD);
>>        ObjectLocker ol(lockObject, THREAD, true);
>>        // prohibited package check assumes all classes loaded from 
>> archive call
>>        // restore_unshareable_info which calls ik->set_package()
>>        ik->restore_unshareable_info(loader_data, protection_domain, 
>> CHECK_NULL);
>>      }
>>
>> - However, when a class loaded from the CDS archive is being redefined
>> there's a shortcut in SystemDictionary::load_shared_class() which
>> instantly returns the new, redefined class, without calling
>> InstanceKlass::restore_unshareable_info() on it:
>>
>>      InstanceKlass* new_ik = 
>> KlassFactory::check_shared_class_file_load_hook(
>>          ik, class_name, class_loader, protection_domain, CHECK_NULL);
>>      if (new_ik != NULL) {
>>        // The class is changed by CFLH. Return the new class. The 
>> shared class is
>>        // not used.
>>        return new_ik;
>>      }
>>
>> - For the well known class java.lang.Object the procedure is a little
>> different. It gets loaded early in
>> SystemDictionary::resolve_preloaded_classes() and for some reason we
>> directly call ConstantPool::restore_unshareable_info() right on its
>> constant pool after it was loaded:
>>
>> #if INCLUDE_CDS
>>    if (UseSharedSpaces) {
>> resolve_wk_klasses_through(WK_KLASS_ENUM_NAME(Object_klass), scan, 
>> CHECK);
>> ...
>>      // Initialize the constant pool for the Object_class
>> Object_klass()->constants()->restore_unshareable_info(CHECK);
>>
>> - This fails if java.lang.Object was redefined, because in that case
>> its constant pool won't have its '_on_stack' flag set to 'true'.
>>
>> - I don't think that this extra call to
>> Object_klass()->constants()->restore_unshareable_info() is required
>> any more, because the call to
>> resolve_wk_klasses_through(WK_KLASS_ENUM_NAME(Object_klass)) already
>> loads java.lang.Object through SystemDictionary::load_shared_class()
>> which will call restore_unshareable_info() on it (if it was not
>> redefined). But I'll have to check this more thoroughly...
>>
>> I'll do that and open a bug for this issue tomorrow.
>>
>> Regards,
>> Volker
>>
>> On Mon, Oct 15, 2018 at 5:32 PM Michael Rasmussen
>> <Michael.Rasmussen at roguewave.com> wrote:
>>> Hi
>>>
>>> I have some questions regarding Class Data Sharing and class file 
>>> transforming agents:
>>>
>>> 1. Is it possible to detect during Agent_OnLoad if CDS is used or not?
>>> Checking for "sharing" in the "java.vm.info" system property, which 
>>> seems to be how the tests are detecting it, is not an option, as 
>>> this property contains bogus information during Agent_OnLoad.
>>>
>>> 2. Should the registration of a ClassFileHook, especially with 
>>> can_generate_all_class_hook_events and 
>>> can_generate_early_class_hook_events capabilities set, disable CDS?
>>>
>>> 3. Is it possible to explicitly disable CDS from a JVM-TI agent 
>>> during Agent_OnLoad?
> Currently, there is no easy to disable CDS from a JVMTI agent that I 
> can think of. We could provide a property for this.
>
> Best regards,
> Jiangli
>>>
>>>
>>> The reason for these questions is that we (JRebel) recently became 
>>> aware of an issue if CDS is used, where transforming some of the 
>>> boot classes causes the JVM to crash.
>>> The simplest example of this is replacing Object's byte in a 
>>> ClassFileLoadHook with a copy of themselves, see inlined example 
>>> agent code below.
>>> I don't know if there are more issues than that, as that's where we 
>>> are currently stuck.
>>>
>>> Running on a 11+28 fastdebug (with classes.jsa file present) it 
>>> produces the following assert:
>>> #  Internal Error (.../src/hotspot/share/oops/constantPool.cpp:325), 
>>> pid=589, tid=590
>>> #  assert(on_stack()) failed: should always be set for shared 
>>> constant pools
>>>
>>> if running with -Xshare:off (or no classes.jsa file), it works 
>>> without any issue.
>>>
>>> Kind regards
>>> Michael Rasmussen
>>>
>>> /* --- snip --- */
>>> #include <string.h>
>>> #include <jni.h>
>>> #include <jvmti.h>
>>>
>>> void JNICALL callback_ClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv* env,
>>>          jclass class_being_redefined, jobject loader, const char* 
>>> name, jobject protection_domain,
>>>          jint class_data_len, const unsigned char* class_data,
>>>          jint* new_class_data_len, unsigned char** new_class_data) {
>>>
>>>          if (strcmp("java/lang/Object", name) == 0) {
>>>                  *new_class_data_len = class_data_len;
>>>                  (*jvmti)->Allocate(jvmti, *new_class_data_len, 
>>> new_class_data);
>>>                  memcpy(*new_class_data, class_data, 
>>> *new_class_data_len);
>>>          }
>>> }
>>>
>>> JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void 
>>> *reserved) {
>>>          jvmtiEnv *jvmti = NULL;
>>>          jint rc = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_9);
>>>
>>>          jvmtiCapabilities caps;
>>>          memset(&caps, 0, sizeof(caps));
>>>
>>>          caps.can_redefine_classes = 1;
>>>          caps.can_generate_all_class_hook_events = 1;
>>>          caps.can_generate_early_vmstart = 1;
>>>          caps.can_generate_early_class_hook_events = 1;
>>>
>>>          (*jvmti)->AddCapabilities(jvmti, &caps);
>>>
>>>          jvmtiEventCallbacks callbacks;
>>>          memset(&callbacks, 0, sizeof(callbacks));
>>>          callbacks.ClassFileLoadHook = &callback_ClassFileLoadHook;
>>>          (*jvmti)->SetEventCallbacks(jvmti, &callbacks, 
>>> sizeof(callbacks));
>>>          (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 
>>> JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
>>>
>>>          return 0;
>>> }
>>> /* --- snap --- */
>



More information about the hotspot-dev mailing list