Search code examples
javajvmclassloaderjvmti

Why does the JVM send multiple JVMTI ClassLoad events for the same class?


I enabled JVMTI ClassLoad events which are supposed to be generated when a class is first loaded. I expected to get this event exactly once per class that was loaded but for some classes it seems to be generated multiple times, e.g. this one I get twice:

ClassLoad: Ljava/util/concurrent/ThreadFactory; loaded by thread 1
ClassLoad: Ljava/util/concurrent/ThreadFactory; loaded by thread 1

In systemDictionary.cpp I found three occurances of JvmtiExport::post_class_load which I assume is the code responsible to execute the callback (if any). They are in

  1. SystemDictionary::resolve_instance_class_or_null
  2. SystemDictionary::parse_stream
  3. SystemDictionary::define_instance_class

But I do not understand the whole flow yet and hence it's not clear why I'm getting the event multiple times.

Is there anything I can do to prevent this or do I need to take care of that myself and adjust the code to ignore more than one event for the same class?


Solution

  • It's OK to see the event twice. This means the class resolution happens twice in the context of different class loaders. First, it is resolved by the Bootstrap class loader, then by the System class loader - sun.misc.Launcher.AppClassLoader.

    The utility from this answer will shed light on the circumstances when such events happen.

    Consider a simple example:

    public class Test {
    
        public void setThreadFactory(ThreadFactory factory) {
        }
    
        public static void main(String[] args) {
        }
    }
    

    When the launcher looks for public static void main(), it creates Method objects for public methods, and this causes resolution of all classes in the signatures of such methods.

    The first ClassLoad event:

    Class loaded: java/util/concurrent/ThreadFactory
      - ClassLoad(_jvmtiEnv*, JNIEnv_*, _jobject*, _jclass*) + 0x69
      - JvmtiExport::post_class_load(JavaThread*, Klass*) + 0x15b
      - SystemDictionary::define_instance_class(instanceKlassHandle, Thread*) + 0x3cc
      - SystemDictionary::find_or_define_instance_class(Symbol*, Handle, instanceKlassHandle, Thread*) + 0x35d
      - SystemDictionary::load_instance_class(Symbol*, Handle, Thread*) + 0x20c
      - SystemDictionary::resolve_instance_class_or_null(Symbol*, Handle, Handle, Thread*) + 0x78c
      - JVM_FindClassFromBootLoader + 0x22b
      - Java_java_lang_ClassLoader_findBootstrapClass + 0x9b
      * java/lang/ClassLoader.findBootstrapClass @ -1
      * java/lang/ClassLoader.findBootstrapClassOrNull @ 12
      * java/lang/ClassLoader.loadClass @ 48
      * java/lang/ClassLoader.loadClass @ 38
      * sun/misc/Launcher$AppClassLoader.loadClass @ 81
      * java/lang/ClassLoader.loadClass @ 3
      * java/lang/Class.getDeclaredMethods0 @ -1
      * java/lang/Class.privateGetDeclaredMethods @ 37
      * java/lang/Class.privateGetMethodRecursive @ 2
      * java/lang/Class.getMethod0 @ 16
      * java/lang/Class.getMethod @ 13
      * sun/launcher/LauncherHelper.validateMainClass @ 12
      * sun/launcher/LauncherHelper.checkAndLoadMain @ 214
    

    The second one:

    Class loaded: java/util/concurrent/ThreadFactory
      - ClassLoad(_jvmtiEnv*, JNIEnv_*, _jobject*, _jclass*) + 0x69
      - JvmtiExport::post_class_load(JavaThread*, Klass*) + 0x15b
      - SystemDictionary::resolve_instance_class_or_null(Symbol*, Handle, Handle, Thread*) + 0x87c
      - SystemDictionary::resolve_or_fail(Symbol*, Handle, Handle, bool, Thread*) + 0x33
      - get_mirror_from_signature(methodHandle, SignatureStream*, Thread*) + 0xc6
      - Reflection::get_parameter_types(methodHandle, int, oopDesc**, Thread*) + 0x18e
      - Reflection::new_method(methodHandle, bool, bool, Thread*) + 0xfc
      - get_class_declared_methods_helper(JNIEnv_*, _jclass*, unsigned char, bool, Klass*, Thread*) + 0x479
      - JVM_GetClassDeclaredMethods + 0xcb
      * java/lang/Class.getDeclaredMethods0 @ -1
      * java/lang/Class.privateGetDeclaredMethods @ 37
      * java/lang/Class.privateGetMethodRecursive @ 2
      * java/lang/Class.getMethod0 @ 16
      * java/lang/Class.getMethod @ 13
      * sun/launcher/LauncherHelper.validateMainClass @ 12
      * sun/launcher/LauncherHelper.checkAndLoadMain @ 214