Class.forName的加载类底层实现

2,191 阅读5分钟

Class的forName的demo

我们平时写代码时可以通过Class.forName传入三个参数类全限定名称.就可以返回Class对象.那么加载类是如何实现的呢? image.png

Class.forName的调用流程

  1. 实现通过Reflection获取调用forName的类,然后直接调用forName0方法进行加载。传入四个参数: 类全名、是否初始化、调用者class的classloader、调用者类.
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

forName0这个方法是native方法调用,那么接下

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

Class#forName0的JVM层的实现

JVM虚拟机中Class.c中Java_java_lang_Class_forName0,这就是Java中forName0的native方法调用所对应的JVM层方法, 这里可以看到第一个参数是JNIEnv,第二个参数是当前类,第三个参数是的类全限定名,第四个参数是是否初始化,第五个参数是加载的classloader实例,第六个参数是调用者的的class。

  1. 下面代码省略了部分代码,保留了主要逻辑,其函数主要调用JVM_FindClassFromCaller查找类全名的class.
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;
    //省略
    cls = JVM_FindClassFromCaller(env, clname, initialize, loader, caller);
    //省略
    return cls;
}

Class#JVM_FindClassFromCaller

  1. 使用传入的classloader去加载这个类全名的Class,并使用调用的class的protection domain。
  2. find_class_from_class_loader从当前的classloader加载class。
JVM_ENTRY(jclass, JVM_FindClassFromCaller(JNIEnv* env, const char* name,jboolean init, jobject loader,jclass caller))
  //省略部分代码
  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_END

Class#find_class_from_class_loader

  1. 调用SystemDictionary的resolve_or_fail查询并解析字节码,加载虚拟机中成为Class对象.
  2. 检查是否需要初始化Class的初始化.
jclass find_class_from_class_loader(JNIEnv* env, Symbol* name, jboolean init, Handle loader, Handle protection_domain,
 jboolean throwError, TRAPS) {
   //省略部分代码
  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());
}

SystemDictionary#resolve_or_fail

  1. 调用resolve_or_null即系class_name所对应的字节码为Klass,并检查异常.
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;
}

SystemDictionary#resolve_or_null

  1. 判断是数组调用resolve_array_class_or_null, 对象则调用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);
  }

SystemDictionary#resolve_instance_class_or_null_helper

  1. 如果是对象类型则忽略类全名中L和;字符,然后调用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);
  }
}

SystemDictionary#resolve_instance_class_or_null

  1. 获取ClassLoaderData的Dictionary,就算类全名的hash值
  2. 如果Dictionary没有查询到类的实例,则获取Classloader的ObjectLocker锁.
  3. 然后获取MutexLocker锁. 再从dictionary中调用find_class传入类的全名的hash值以及类全名,如果已经加载,则赋值loaded_class。
  4. 获取PlaceholderEntry,继续处理加载父类。
  5. 如果loaded_class为空,则调用load_instance_class加载类实例。
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;
}

SystemDictionary#load_instance_class

  1. 调用load_instance_class_impl去加载Class对象.
  2. 记录class到classloader的ClassLoaderData对象中.并更新SystemDictionary。
  3. post_class_load就是类加载的后置处理(调用JVMTIExport接口,这是java开发者暴露的接口调用)
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;
}

SystemDictionary#load_instance_class_impl

  1. 如果classloader为空,则利用vm的bootstrap classloader的加载类.
  2. 如果classloader不为空,则通过JavaCalls::call_virtual调用用户定义的classloader的loadClass方法去加载类。
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;
  }
}

总结
今天主要是对Class.forName底层实现的分析。

  • 通过计算类全名的hash值和类全名底层的loader所对应的SystemDictionary的查询类是否已经加载。
  • 如果没有查询到,判断是否存在classloader,来决定是bootstrap classloader还是JavaCalls#call调用自定义的classloader的loadClass去加载类。