Class的forName的demo

When we write code, we can pass in the fully qualified names of the three parameter classes as class.forname. You can return a Class object. So how does loading a class work?

Call flow of Class. ForName

  1. The implementation gets the class that calls forName through Reflection and then loads it directly by calling the forName0 method. Four arguments are passed: the full name of the class, whether to initialize it, the classloader of the caller class, and the caller class.
public static Class<? > forName(String className) throws ClassNotFoundException { Class<? > caller = Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller); }Copy the code

ForName0 is a native method call, so go ahead

private static native Class<? > forName0(String name, boolean initialize, ClassLoader loader, Class<? > caller) throws ClassNotFoundException;Copy the code

JVM layer implementation of Class#forName0

Java_java_lang_Class_forName0 is the native method of Java. The first parameter is JNIEnv, and the second parameter is the current Class. The third parameter is the fully qualified name of the class, the fourth parameter is whether to initialize, the fifth parameter is the classLoader instance to load, and the sixth parameter is the class of the caller.

  1. The following code omits some of the code and preserves the main logic. Its function mainly calls JVM_FindClassFromCaller to look up the class’s full name.
JNIEXPORT jclass JNICALL Java_java_lang_Class_forName0(JNIEnv *env, jclass this, jstring classname,jboolean initialize, jobject loader, jclass caller) { char *clname; jclass cls = 0; // omit CLS = JVM_FindClassFromCaller(env, clname, initialize, loader, caller); // omit return CLS; }Copy the code

Class#JVM_FindClassFromCaller

  1. Use the classloader passed in to load the Class’s full name and use the protection domain of the calling Class.
  2. Find_class_from_class_loader loads the class from the current classloader.
JVM_ENTRY(jclass, JVM_FindClassFromCaller(JNIEnv* env, const char* name,jboolean init, Handle h_loader(THREAD, loader_oop); Handle h_loader(THREAD, loader_oop); Handle h_prot(THREAD, protection_domain); jclass result = find_class_from_class_loader(env, h_name, init, h_loader,h_prot, false, THREAD); if (log_is_enabled(Debug, class, resolve) && result ! = NULL) {trace_class_resolution(java_lang_Class::as_Klass(JNIHandles::resolve_non_null(result))); } return result; JVM_ENDCopy the code

Class#find_class_from_class_loader

  1. Call SystemDictionary’s resolve_or_fail query and parse the bytecode to load the virtual machine as a Class object.
  2. Check whether initialization of the Class is required.
jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init, Handle loader, Handle protection_domain, Jboolean throwError, TRAPS) {/ / omit part of the code Klass * Klass = SystemDictionary: : resolve_or_fail (name, loader, protection_domain, throwError ! = 0, CHECK_NULL); // Check if we should initialize the class if (init && klass->is_instance_klass()) { klass->initialize(CHECK_NULL); } return (jclass) JNIHandles::make_local(THREAD, klass->java_mirror()); }Copy the code

SystemDictionary#resolve_or_fail

  1. Call resolve_or_NULL if the bytecode corresponding to class_name is Klass and check the exception.
Klass* SystemDictionary::resolve_or_fail(Symbol* class_name, Handle class_loader, Handle protection_domain, bool throw_error, TRAPS) {
  Klass* klass = resolve_or_null(class_name, class_loader, protection_domain, THREAD);
  // Check for pending exception or null klass, and throw exception
  if (HAS_PENDING_EXCEPTION || klass == NULL) {
    handle_resolution_exception(class_name, throw_error, CHECK_NULL);
  }
  return klass;
}
Copy the code

SystemDictionary#resolve_or_null

  1. The array is called resolve_array_class_or_NULL, and the object is called resolve_instance_class_or_NULl_helper
Klass* SystemDictionary::resolve_or_null(Symbol* class_name, Handle class_loader, Handle protection_domain, TRAPS) {
  if (Signature::is_array(class_name)) {
    return resolve_array_class_or_null(class_name, class_loader, protection_domain, THREAD);
  } else {
    return resolve_instance_class_or_null_helper(class_name, class_loader, protection_domain, THREAD);
  }
Copy the code

SystemDictionary#resolve_instance_class_or_null_helper

  1. Ignore L and in the full name of the class if it is an object type; Character, and continue parsing with a call to resolve_instance_class_or_NULL.
SystemDictionary::resolve_instance_class_or_null_helper(Symbol* class_name, Handle class_loader,Handle protection_domain, TRAPS) { assert(class_name ! = NULL && ! Signature::is_array(class_name), "must be"); if (Signature::has_envelope(class_name)) { ResourceMark rm(THREAD); // Ignore wrapping L and ; . TempNewSymbol name = SymbolTable::new_symbol(class_name->as_C_string() + 1, class_name->utf8_length() - 2); return resolve_instance_class_or_null(name, class_loader, protection_domain, THREAD); } else { return resolve_instance_class_or_null(class_name, class_loader, protection_domain, THREAD); }}Copy the code

SystemDictionary#resolve_instance_class_or_null

  1. Get the Dictionary of ClassLoaderData, even the hash value of the class’s full name
  2. If the Dictionary does not query for an instance of the class, the ObjectLocker lock of the Classloader is obtained.
  3. Then get the MutexLocker lock. Call find_class from the dictionary and pass in the hash value of the full name of the class and the full name of the class, if loaded, then assign loaded_class.
  4. Retrieve the PlaceholderEntry and proceed with loading the parent.
  5. If loaded_class is empty, load_instance_class is called to load the class instance.
SystemDictionary::resolve_instance_class_or_null(Symbol* name,Handle class_loader,Handle protection_domain,TRAPS) {
  assert(name != NULL && !Signature::is_array(name) &&
         !Signature::has_envelope(name), "invalid class name");
  EventClassLoad class_load_start_event;
  HandleMark hm(THREAD);
  class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
 ClassLoaderData* loader_data = register_loader(class_loader);
  Dictionary* dictionary = loader_data->dictionary();
  unsigned int name_hash = dictionary->compute_hash(name);
  InstanceKlass* probe = dictionary->find(name_hash, name, protection_domain);
  if (probe != NULL) return probe;
  Handle lockObject = get_loader_lock_or_null(class_loader);
  ObjectLocker ol(lockObject, THREAD);
  
  bool super_load_in_progress  = false;
  InstanceKlass* loaded_class = NULL;
  Symbol* superclassname = NULL;
  assert(THREAD->can_call_java(),
         "can not load classes with compiler thread: class=%s, classloader=%s",
         name->as_C_string(),
         class_loader.is_null() ? "null" : class_loader->klass()->name()->as_C_string());

  assert(placeholders()->compute_hash(name) == name_hash, "they're the same hashcode");
  // Check again (after locking) if the class already exists in SystemDictionary
  {
    MutexLocker mu(THREAD, SystemDictionary_lock);
    InstanceKlass* check = dictionary->find_class(name_hash, name);
    if (check != NULL) {
      // InstanceKlass is already loaded, but we still need to check protection domain below.
      loaded_class = check;
    } else {
      PlaceholderEntry* placeholder = placeholders()->get_entry(name_hash, name, loader_data);
      if (placeholder != NULL && placeholder->super_load_in_progress()) {
         super_load_in_progress = true;
         superclassname = placeholder->supername();
         assert(superclassname != NULL, "superclass has to have a name");
      }
    }
  }
  // If the class is in the placeholder table with super_class set,
  // handle superclass loading in progress.
  if (super_load_in_progress) {
    handle_parallel_super_load(name, superclassname,
                               class_loader,
                               protection_domain,
                               CHECK_NULL);
  }
  bool throw_circularity_error = false;
  if (loaded_class == NULL) {
       bool load_placeholder_added = false;.
    // case 4. traditional class loaders that break the classloader object lock
    // as a legacy deadlock workaround. Detection of this case requires that
    //    this check is done while holding the classloader object lock,
    //    and that lock is still held when calling classloader's loadClass.
    //    For these classloaders, we ensure that the first requestor
    //    completes the load and other requestors wait for completion.
    {
      MutexLocker mu(THREAD, SystemDictionary_lock);
      if (should_wait_for_loading(class_loader)) {
        loaded_class = handle_parallel_loading(THREAD,
                                               name_hash,
                                               name,
                                               loader_data,
                                               lockObject,                                       &throw_circularity_error);
      }
      // Recheck if the class has been loaded for all class loader cases and
      // add a LOAD_INSTANCE placeholder while holding the SystemDictionary_lock.
      if (!throw_circularity_error && loaded_class == NULL) {
        InstanceKlass* check = dictionary->find_class(name_hash, name);
        if (check != NULL) {
          loaded_class = check;
        } else if (should_wait_for_loading(class_loader)) {
          // Add the LOAD_INSTANCE token. Threads will wait on loading to complete for this thread.
          PlaceholderEntry* newprobe = placeholders()->find_and_add(name_hash, name, loader_data,               PlaceholderTable::LOAD_INSTANCE,NULL,THREAD);
          load_placeholder_added = true;
        }
      }
    }
    // Must throw error outside of owning lock
    if (throw_circularity_error) {
      assert(!HAS_PENDING_EXCEPTION && !load_placeholder_added, "circularity error cleanup");
      ResourceMark rm(THREAD);
 THROW_MSG_NULL(vmSymbols::java_lang_ClassCircularityError(), name->as_C_string());
    }
    // Be careful when modifying this code: once you have run
    // placeholders()->find_and_add(PlaceholderTable::LOAD_INSTANCE),
    // you need to find_and_remove it before returning.
    // So be careful to not exit with a CHECK_ macro between these calls.
    if (loaded_class == NULL) {
      // Do actual loading
      loaded_class = load_instance_class(name_hash, name, class_loader, THREAD);
    }
    if (load_placeholder_added) {
      // clean up placeholder entries for LOAD_INSTANCE success or error
      // This brackets the SystemDictionary updates for both defining
      // and initiating loaders
      MutexLocker mu(THREAD, SystemDictionary_lock);
      placeholders()->find_and_remove(name_hash, name, loader_data, PlaceholderTable::LOAD_INSTANCE, THREAD);
      SystemDictionary_lock->notify_all();
    }
  }
  if (HAS_PENDING_EXCEPTION || loaded_class == NULL) {
    return NULL;
  }
  if (class_load_start_event.should_commit()) {
    post_class_load_event(&class_load_start_event, loaded_class, loader_data);
  }
  // Make sure we have the right class in the dictionary
  DEBUG_ONLY(verify_dictionary_entry(name, loaded_class));
  // Check if the protection domain is present it has the right access
  if (protection_domain() != NULL) {
    // Verify protection domain. If it fails an exception is thrown
    dictionary->validate_protection_domain(name_hash, loaded_class, class_loader, protection_domain, CHECK_NULL);
  }
  return loaded_class;
}
Copy the code

SystemDictionary#load_instance_class

  1. Call load_instance_class_impl to load the Class object.
  2. Record the class to the ClassLoaderData object of the classloader. And update SystemDictionary.
  3. Post_class_load is the post-processing of class loading (calling the JVMTIExport interface, which is the interface call exposed by the Java developer)
InstanceKlass* SystemDictionary::load_instance_class(unsigned int name_hash, Symbol* name, Handle class_loader,TRAPS) { InstanceKlass* loaded_class = load_instance_class_impl(name, class_loader, CHECK_NULL); // If everything was OK (no exceptions, no null return value), and // class_loader is NOT the defining loader, do a little more bookkeeping. if (loaded_class ! = NULL && loaded_class->class_loader() ! = class_loader()) { check_constraints(name_hash, loaded_class, class_loader, false, CHECK_NULL); // Record dependency for non-parent delegation. // This recording keeps the defining class loader of the klass (loaded_class) found // from being unloaded while the initiating class loader is loaded // even if the reference to the defining class loader is dropped // before references to the initiating class loader. ClassLoaderData* loader_data = class_loader_data(class_loader); loader_data->record_dependency(loaded_class); { // Grabbing the Compile_lock prevents systemDictionary updates // during compilations. MutexLocker mu(THREAD, Compile_lock); update_dictionary(name_hash, loaded_class, class_loader); } if (JvmtiExport::should_post_class_load()) { JvmtiExport::post_class_load(THREAD, loaded_class); } } return loaded_class; }Copy the code

SystemDictionary#load_instance_class_impl

  1. If the classloader is empty, the vm’s Bootstrap classloader is used to load the class.
  2. If the classloader is not empty, the class is loaded by calling the loadClass method of the user-defined classloader via JavaCalls::call_virtual.
InstanceKlass* SystemDictionary::load_instance_class_impl(Symbol* class_name, Handle class_loader, TRAPS) { if (class_loader.is_null()) { ResourceMark rm(THREAD); PackageEntry* pkg_entry = NULL; bool search_only_bootloader_append = false; ClassLoaderData *loader_data = class_loader_data(class_loader); // Search for classes in the CDS archive. InstanceKlass* k = NULL; #if INCLUDE_CDS if (UseSharedSpaces) { InstanceKlass* ik = SystemDictionaryShared::find_builtin_class(class_name); if (ik ! = NULL && ik->is_shared_boot_class() && ! ik->shared_loading_failed()) { SharedClassLoadingMark slm(THREAD, ik); k = load_shared_class(ik, class_loader, Handle(), NULL, pkg_entry, CHECK_NULL); } } #endif if (k == NULL) { // Use VM class loader PerfTraceTime vmtimer(ClassLoader::perf_sys_classload_time()); k = ClassLoader::load_class(class_name, search_only_bootloader_append, CHECK_NULL); } // find_or_define_instance_class may return a different InstanceKlass if (k ! = NULL) { CDS_ONLY(SharedClassLoadingMark slm(THREAD, k);) k = find_or_define_instance_class(class_name, class_loader, k, CHECK_NULL); } return k; } else { // Use user specified class loader to load class. Call loadClass operation on class_loader. ResourceMark rm(THREAD); JavaThread* jt = THREAD; // Translate to external class name format, i.e., convert '/' chars to '.' Handle string = java_lang_String::externalize_classname(class_name, CHECK_NULL); JavaValue result(T_OBJECT); InstanceKlass* spec_klass = vmClasses::ClassLoader_klass(); // Call public unsynchronized loadClass(String) directly for all class loaders. // For parallelCapable class loaders, JDK >=7, loadClass(String, boolean) will // acquire a class-name based lock rather than the class loader object lock. // JDK < 7 already acquire the class loader lock in loadClass(String, boolean). JavaCalls::call_virtual(&result, class_loader, spec_klass, vmSymbols::loadClass_name(), vmSymbols::string_class_signature(), string, CHECK_NULL); assert(result.get_type() == T_OBJECT, "just checking"); oop obj = result.get_oop(); // Primitive classes return null since forName() can not be // used to obtain any of the Class objects representing primitives or void if ((obj ! = NULL) && ! (java_lang_Class::is_primitive(obj))) { InstanceKlass* k = InstanceKlass::cast(java_lang_Class::as_Klass(obj)); // For user defined Java class loaders, check that the name returned is // the same as that requested. This check is done for the bootstrap // loader when parsing the class file. if (class_name == k->name()) { return k; } } // Class is not found or has the wrong name, return NULL return NULL; }}Copy the code

Summary today is mainly the analysis of the underlying implementation of Class. ForName.

  • Check whether the class has been loaded by calculating the hash value of the class name and the corresponding SystemDictionary of the loader underlying the class name.
  • Bootstrap classloader or JavaCalls#call the loadClass of the custom classloader to load the class.