Java中的类加载过程

Java中的类加载器一文中介绍过Java中的类加载器以及它们的创建和初始化过程,它们本身之间并没有很强的关系,而且应用类加载器和扩展类加载器是由Java代码实现在JDK中的,而启动类加载器则是通过C++代码实现在Hotspot VM源码中的。而类加载过程则会串联起来这几个类加载器,而这正是本文要分析的内容。


在前面介绍类加载器时就讲到Hotspot VM会调用LauncherHelper类的checkAndLoadMain方法来加载主类。

java
jdk/src/share/classes/sun/launcher/LauncherHelper.java
// 获取系统类加载器,也就是AppClassLoader
private static final ClassLoader scloader = ClassLoader.getSystemClassLoader();
/*
 * 检查和加载主类
 */
public static Class<?> checkAndLoadMain(boolean printToStderr,
                                        int mode,
                                        String what) {
    // 初始化输出
    initOutput(printToStderr);
    // get the class name
    // 获取类名称
    String cn = null;
    // 根据启动模式按照不同形式获取主类
    switch (mode) {
        case LM_CLASS:
            cn = what;
            break;
        case LM_JAR:
            // 从jar包中获取主类
            cn = getMainClassFromJar(what);
            break;
        default:
            // should never happen
            throw new InternalError("" + mode + ": Unknown launch mode");
    }
    // 将类文件路径中的"/"替换为“.”,将其转为包名的形式
    cn = cn.replace('/', '.');
    Class<?> mainClass = null;
    try {
        /*
         * 根据主类名称加载主类,这里的scloader是指系统类加载器。
         * 一般情况下,这里的scloader也就是应用类加载器。
         */
        mainClass = scloader.loadClass(cn);
    } catch (NoClassDefFoundError | ClassNotFoundException cnfe) {
        if (System.getProperty("os.name", "").contains("OS X")
            && Normalizer.isNormalized(cn, Normalizer.Form.NFD)) {
            try {
                // On Mac OS X since all names with diacretic symbols are given as decomposed it
                // is possible that main class name comes incorrectly from the command line
                // and we have to re-compose it
                mainClass = scloader.loadClass(Normalizer.normalize(cn, Normalizer.Form.NFC));
            } catch (NoClassDefFoundError | ClassNotFoundException cnfe1) {
                abort(cnfe, "java.launcher.cls.error1", cn);
            }
        } else {
            abort(cnfe, "java.launcher.cls.error1", cn);
        }
    }
    // set to mainClass
    appClass = mainClass;

    /*
     * Check if FXHelper can launch it using the FX launcher. In an FX app,
     * the main class may or may not have a main method, so do this before
     * validating the main class.
     */
    if (mainClass.equals(FXHelper.class) ||
            FXHelper.doesExtendFXApplication(mainClass)) {
        // Will abort() if there are problems with the FX runtime
        FXHelper.setFXLaunchParameters(what, mode);
        return FXHelper.class;
    }

    // 验证主类
    validateMainClass(mainClass);
    return mainClass;
}

在该方法中,最重要的是调用了scloaderloadClass方法,该方法中实现了所谓的双亲委派机制。

虽然应用类加载器重写了该方法,但逻辑主要是与安全管理器相关,Java 17中已经移除了安全管理器,所以本文不会介绍,重点关注父类ClassLoader中的实现即可。

双亲委派机制

java
jdk/src/share/classes/java/lang/ClassLoader.java
public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}
/*
 * 该方法保证了双亲委派模型,如果自定义类加载器要破坏双亲委派,则重写该方法。
 * 如果不破坏,则重写findClass(name)方法。
 */
protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    // 锁住目标类名,避免同一个类被重复加载
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        /*
         * 检查类是否已经被当前类加载器加载过了,
         * 会调用本地方法findLoadedClass0。
         */
        Class<?> c = findLoadedClass(name);
        if (c == null) { // 还没被加载过
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    // 先交给父类加载器来加载
                    c = parent.loadClass(name, false);
                } else {
                    /*
                     * 如果父类加载器是null,则交给启动类加载器来加载。
                     * 调用本地方法findBootstrapClass。
                     */
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            // 如果父加载器没加载到,当前类加载器才加载
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                /*
                 * 当前类加载器尝试加载类。
                 * 该方法是模板方法,由子类实现,具体请参考URLClassLoader。
                 * 最终会调用到该类中定义的defineClass,然后调用本地方法defineClass1。
                 */
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

该方法中,调用findLoadedClass方法来判断类是否已被当前类加载器加载过,如果没有被加载过,则判断当前类加载器是否有父类加载器(组合关系,不是继承关系),如果有则通过父类加载器来加载,如果没有则调用findBootstrapClassOrNull方法来让启动类加载器加载。如果父类加载器没有加载到,再调用findClass方法用当前的类加载器加载。这正是双亲委派机制的体现,即优先让父级类加载器加载,如果父类加载器没有加载到,则当前类加载器才去尝试加载。 最后,如果需要在加载时解析类,则进行类解析;默认是false,所以不会在加载时解析类。

接下来我们分析一下上面三个方法,这三个方法最终都会调用到本地方法,所以下面的分析会从Java代码到C++代码“一镜到底”。

启动类加载器加载类

java
jdk/src/share/classes/java/lang/ClassLoader.java
private Class<?> findBootstrapClassOrNull(String name)
{
    if (!checkName(name)) return null;

    return findBootstrapClass(name);
}
// return null if not found
private native Class<?> findBootstrapClass(String name);

先是调用了checkName方法来判断类的名称是否合法。

findBootstrapClassOrNull
checkName
<
>
java
jdk/src/share/classes/java/lang/ClassLoader.java
private Class<?> findBootstrapClassOrNull(String name)
{
    if (!checkName(name)) return null;

    return findBootstrapClass(name);
}
// return null if not found
private native Class<?> findBootstrapClass(String name);
java
jdk/src/share/classes/java/lang/ClassLoader.java
private boolean checkName(String name) {
    if ((name == null) || (name.length() == 0))
        return true;
    /*
     * 类名中不能包含/,如果虚拟机不允许数组语法,那么名称中不能包含字符[
     */
    if ((name.indexOf('/') != -1)
        || (!VM.allowArraySyntax() && (name.charAt(0) == '[')))
        return false;
    return true;
}

接下来就是调用本地方法findBootstrapClass了。

c
jdk/src/share/native/java/lang/ClassLoader.c
/*
 * Returns NULL if class not found.
 */
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_findBootstrapClass(JNIEnv *env, jobject loader,
                                              jstring classname)
{
    char *clname;
    jclass cls = 0;
    char buf[128];

    if (classname == NULL) {
        return 0;
    }

    clname = getUTF(env, classname, buf, sizeof(buf));
    if (clname == NULL) {
        JNU_ThrowOutOfMemoryError(env, NULL);
        return NULL;
    }
    VerifyFixClassname(clname);

    // 验证类名
    if (!VerifyClassname(clname, JNI_TRUE)) {  /* expects slashed name */
        goto done;
    }

    // 查找由启动类加载器加载的类
    cls = JVM_FindClassFromBootLoader(env, clname);

 done:
    if (clname != buf) {
        free(clname);
    }

    return cls;
}

继续调用JVM_FindClassFromBootLoader函数,进入Hotspot VM的实现。

cpp
hotspot/src/share/vm/prims/jvm.cpp
JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
                                              const char* name))
  JVMWrapper2("JVM_FindClassFromBootLoader %s", name);

  // Java libraries should ensure that name is never null...
  // 检查类名是否合法
  if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
    // It's impossible to create this class;  the name cannot fit
    // into the constant pool.
    return NULL;
  }

  TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
  // 查找目标类,没找到会返回NULL
  Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
  if (k == NULL) {
    return NULL;
  }

  if (TraceClassResolution) {
    trace_class_resolution(k);
  }
  // 将Klass实例转换成java.lang.Class对象
  return (jclass) JNIHandles::make_local(env, k->java_mirror());
JVM_END

上面函数中调用SystemDictionaryresolve_or_null函数来加载或获取类,返回的是一个表示Java类的C++类型Klass的对象,最后会将该对象转换为java.lang.Class对象。

cpp
hotspot/src/share/vm/classfile/systemDictionary.cpp
Klass* SystemDictionary::resolve_or_null(Symbol* class_name, TRAPS) {
  /*
   * 注意,这里传入的两个Handle都是对null的封装
   */
  return resolve_or_null(class_name, Handle(), Handle(), THREAD);
}
Klass* SystemDictionary::resolve_or_null(Symbol* class_name, Handle class_loader, Handle protection_domain, TRAPS) {
  assert(!THREAD->is_Compiler_thread(),
         err_msg("can not load classes with compiler thread: class=%s, classloader=%s",
                 class_name->as_C_string(),
                 class_loader.is_null() ? "null" : class_loader->klass()->name()->as_C_string()));
  if (FieldType::is_array(class_name)) { // 数组的情况
    return resolve_array_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
  } else if (FieldType::is_obj(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, CHECK_NULL);
    return resolve_instance_class_or_null(name, class_loader, protection_domain, CHECK_NULL);
  } else {
    return resolve_instance_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
  }
}

在这里,按照是普通类还是数组类,调用了不同的方法来加载类。

加载普通类

cpp
hotspot/src/share/vm/classfile/systemDictionary.cpp
Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
                                                        Handle class_loader,
                                                        Handle protection_domain,
                                                        TRAPS) {
  assert(name != NULL && !FieldType::is_array(name) &&
         !FieldType::is_obj(name), "invalid class name");

  Ticks class_load_start_time = Ticks::now();

  // UseNewReflection
  // Fix for 4474172; see evaluation for more details
  // 获取类加载器
  class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
  // 获取类加载器的数据对象
  ClassLoaderData *loader_data = register_loader(class_loader, CHECK_NULL);

  // Do lookup to see if class already exist and the protection domain
  // has the right access
  // This call uses find which checks protection domain already matches
  // All subsequent calls use find_class, and set has_loaded_class so that
  // before we return a result we call out to java to check for valid protection domain
  // to allow returning the Klass* and add it to the pd_set if it is valid
  // 计算哈希值
  unsigned int d_hash = dictionary()->compute_hash(name, loader_data);
  // 根据哈希值找到在哈希表中的下标
  int d_index = dictionary()->hash_to_index(d_hash);
  // 根据下标查找类对应的Klass实例
  Klass* probe = dictionary()->find(d_index, d_hash, name, loader_data,
                                      protection_domain, THREAD);
  // 如果找到则直接返回
  if (probe != NULL) return probe;


  // Non-bootstrap class loaders will call out to class loader and
  // define via jvm/jni_DefineClass which will acquire the
  // class loader object lock to protect against multiple threads
  // defining the class in parallel by accident.
  // This lock must be acquired here so the waiter will find
  // any successful result in the SystemDictionary and not attempt
  // the define
  // ParallelCapable Classloaders and the bootstrap classloader,
  // or all classloaders with UnsyncloadClass do not acquire lock here
  bool DoObjectLock = true;
  // 是否是并行加载
  if (is_parallelCapable(class_loader)) {
    DoObjectLock = false;
  }

  unsigned int p_hash = placeholders()->compute_hash(name, loader_data);
  int p_index = placeholders()->hash_to_index(p_hash);

  // Class is not in SystemDictionary so we have to do loading.
  // Make sure we are synchronized on the class loader before we proceed
  // 获取加载器锁对象
  Handle lockObject = compute_loader_lock_object(class_loader, THREAD);
  check_loader_lock_contention(lockObject, THREAD);
  ObjectLocker ol(lockObject, THREAD, DoObjectLock);

  // Check again (after locking) if class already exist in SystemDictionary
  bool class_has_been_loaded   = false;
  bool super_load_in_progress  = false;
  bool havesupername = false;
  instanceKlassHandle k;
  PlaceholderEntry* placeholder;
  Symbol* superclassname = NULL;

  {
    // 加锁,要考虑到并行加载的情况
    MutexLocker mu(SystemDictionary_lock, THREAD);
    // 再次查找目标类
    Klass* check = find_class(d_index, d_hash, name, loader_data);
    if (check != NULL) {
      // Klass is already loaded, so just return it
      class_has_been_loaded = true;
      k = instanceKlassHandle(THREAD, check);
    } else {
      placeholder = placeholders()->get_entry(p_index, p_hash, name, loader_data);
      // 如果父类在加载过程中
      if (placeholder && placeholder->super_load_in_progress()) {
         super_load_in_progress = true;
        // 如果有父类
         if (placeholder->havesupername() == true) {
           superclassname = placeholder->supername();
           havesupername = true;
         }
      }
    }
  }

  // If the class is in the placeholder table, class loading is in progress
  if (super_load_in_progress && havesupername==true) {
    // 处理父类加载
    k = SystemDictionary::handle_parallel_super_load(name, superclassname,
        class_loader, protection_domain, lockObject, THREAD);
    if (HAS_PENDING_EXCEPTION) {
      return NULL;
    }
    // 如果父类已经加载完毕
    if (!k.is_null()) {
      class_has_been_loaded = true;
    }
  }

  // 默认不抛出循环依赖错误
  bool throw_circularity_error = false;

  /*
   * 如果没找到,需要尝试对类进行加载
   * 加载时需要考虑的因素较多,包括解决并行加载、触发父类的加载和域权限的验证等
   */
  if (!class_has_been_loaded) {
    bool load_instance_added = false;

    // add placeholder entry to record loading instance class
    // Five cases:
    // All cases need to prevent modifying bootclasssearchpath
    // in parallel with a classload of same classname
    // Redefineclasses uses existence of the placeholder for the duration
    // of the class load to prevent concurrent redefinition of not completely
    // defined classes.
    // case 1. traditional classloaders that rely on the classloader object lock
    //   - no other need for LOAD_INSTANCE
    // case 2. traditional classloaders that break the classloader object lock
    //    as a 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.
    // case 3. UnsyncloadClass - don't use objectLocker
    //    With this flag, we allow parallel classloading of a
    //    class/classloader pair
    // case4. Bootstrap classloader - don't own objectLocker
    //    This classloader supports parallelism at the classloader level,
    //    but only allows a single load of a class/classloader pair.
    //    No performance benefit and no deadlock issues.
    // case 5. parallelCapable user level classloaders - without objectLocker
    //    Allow parallel classloading of a class/classloader pair

    {
      // 再次加锁
      MutexLocker mu(SystemDictionary_lock, THREAD);
      if (class_loader.is_null() || !is_parallelCapable(class_loader)) {
        PlaceholderEntry* oldprobe = placeholders()->get_entry(p_index, p_hash, name, loader_data);
        if (oldprobe) {
          // only need check_seen_thread once, not on each loop
          // 6341374 java/lang/Instrument with -Xcomp
          if (oldprobe->check_seen_thread(THREAD, PlaceholderTable::LOAD_INSTANCE)) {
            throw_circularity_error = true;
          } else {
            // case 1: traditional: should never see load_in_progress.
            while (!class_has_been_loaded && oldprobe && oldprobe->instance_load_in_progress()) {

              // case 4: bootstrap classloader: prevent futile classloading,
              // wait on first requestor
              if (class_loader.is_null()) {
                SystemDictionary_lock->wait();
              } else {
              // case 2: traditional with broken classloader lock. wait on first
              // requestor.
                double_lock_wait(lockObject, THREAD);
              }
              // Check if classloading completed while we were waiting
              // 查找类
              Klass* check = find_class(d_index, d_hash, name, loader_data);
              // 找到类
              if (check != NULL) {
                // Klass is already loaded, so just return it
                // 则封装为Klass的handle(句柄)对象
                k = instanceKlassHandle(THREAD, check);
                class_has_been_loaded = true;
              }
              // check if other thread failed to load and cleaned up
              oldprobe = placeholders()->get_entry(p_index, p_hash, name, loader_data);
            }
          }
        }
      }
      // All cases: add LOAD_INSTANCE holding SystemDictionary_lock
      // case 3: UnsyncloadClass || case 5: parallelCapable: allow competing threads to try
      // LOAD_INSTANCE in parallel

      if (!throw_circularity_error && !class_has_been_loaded) {
        PlaceholderEntry* newprobe = placeholders()->find_and_add(p_index, p_hash, name, loader_data, PlaceholderTable::LOAD_INSTANCE, NULL, THREAD);
        load_instance_added = true;
        // For class loaders that do not acquire the classloader object lock,
        // if they did not catch another thread holding LOAD_INSTANCE,
        // need a check analogous to the acquire ObjectLocker/find_class
        // i.e. now that we hold the LOAD_INSTANCE token on loading this class/CL
        // one final check if the load has already completed
        // class loaders holding the ObjectLock shouldn't find the class here
        Klass* check = find_class(d_index, d_hash, name, loader_data);
        if (check != NULL) {
        // Klass is already loaded, so return it after checking/adding protection domain
          k = instanceKlassHandle(THREAD, check);
          class_has_been_loaded = true;
        }
      }
    }

    // must throw error outside of owning lock
    if (throw_circularity_error) {
      assert(!HAS_PENDING_EXCEPTION && load_instance_added == false,"circularity error cleanup");
      ResourceMark rm(THREAD);
      THROW_MSG_NULL(vmSymbols::java_lang_ClassCircularityError(), name->as_C_string());
    }

    // 还是没加载到,则再次尝试加载
    if (!class_has_been_loaded) {

      // Do actual loading
      // 加载类
      k = load_instance_class(name, class_loader, THREAD);

      // For UnsyncloadClass only
      // If they got a linkageError, check if a parallel class load succeeded.
      // If it did, then for bytecode resolution the specification requires
      // that we return the same result we did for the other thread, i.e. the
      // successfully loaded InstanceKlass
      // Should not get here for classloaders that support parallelism
      // with the new cleaner mechanism, even with AllowParallelDefineClass
      // Bootstrap goes through here to allow for an extra guarantee check
      if (UnsyncloadClass || (class_loader.is_null())) {
        if (k.is_null() && HAS_PENDING_EXCEPTION
          && PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) {
          MutexLocker mu(SystemDictionary_lock, THREAD);
          Klass* check = find_class(d_index, d_hash, name, loader_data);
          if (check != NULL) {
            // Klass is already loaded, so just use it
            k = instanceKlassHandle(THREAD, check);
            CLEAR_PENDING_EXCEPTION;
            guarantee((!class_loader.is_null()), "dup definition for bootstrap loader?");
          }
        }
      }

      // If everything was OK (no exceptions, no null return value), and
      // class_loader is NOT the defining loader, do a little more bookkeeping.
      if (!HAS_PENDING_EXCEPTION && !k.is_null() &&
        k->class_loader() != class_loader()) {

        check_constraints(d_index, d_hash, k, class_loader, false, THREAD);

        // Need to check for a PENDING_EXCEPTION again; check_constraints
        // can throw and doesn't use the CHECK macro.
        if (!HAS_PENDING_EXCEPTION) {
          { // Grabbing the Compile_lock prevents systemDictionary updates
            // during compilations.
            MutexLocker mu(Compile_lock, THREAD);
            // 更新字典
            update_dictionary(d_index, d_hash, p_index, p_hash,
                              k, class_loader, THREAD);
          }

          if (JvmtiExport::should_post_class_load()) {
            Thread *thread = THREAD;
            assert(thread->is_Java_thread(), "thread->is_Java_thread()");
            JvmtiExport::post_class_load((JavaThread *) thread, k());
          }
        }
      }
    } // load_instance_class loop

    if (HAS_PENDING_EXCEPTION) {
      // An exception, such as OOM could have happened at various places inside
      // load_instance_class. We might have partially initialized a shared class
      // and need to clean it up.
      if (class_loader.is_null()) {
        // In some cases k may be null. Let's find the shared class again.
        instanceKlassHandle ik(THREAD, find_shared_class(name));
        if (ik.not_null()) {
          if (ik->class_loader_data() == NULL) {
            // We didn't go as far as Klass::restore_unshareable_info(),
            // so nothing to clean up.
          } else {
            Klass *kk;
            {
              MutexLocker mu(SystemDictionary_lock, THREAD);
              kk = find_class(d_index, d_hash, name, ik->class_loader_data());
            }
            if (kk != NULL) {
              // No clean up is needed if the shared class has been entered
              // into system dictionary, as load_shared_class() won't be called
              // again.
            } else {
              // This must be done outside of the SystemDictionary_lock to
              // avoid deadlock.
              //
              // Note that Klass::restore_unshareable_info (called via
              // load_instance_class above) is also called outside
              // of SystemDictionary_lock. Other threads are blocked from
              // loading this class because they are waiting on the
              // SystemDictionary_lock until this thread removes
              // the placeholder below.
              //
              // This need to be re-thought when parallel-capable non-boot
              // classloaders are supported by CDS (today they're not).
              clean_up_shared_class(ik, class_loader, THREAD);
            }
          }
        }
      }
    }

    if (load_instance_added == true) {
      // clean up placeholder entries for LOAD_INSTANCE success or error
      // This brackets the SystemDictionary updates for both defining
      // and initiating loaders
      MutexLocker mu(SystemDictionary_lock, THREAD);
      placeholders()->find_and_remove(p_index, p_hash, name, loader_data, PlaceholderTable::LOAD_INSTANCE, THREAD);
      SystemDictionary_lock->notify_all();
    }
  }

  if (HAS_PENDING_EXCEPTION || k.is_null()) {
    return NULL;
  }

  post_class_load_event(class_load_start_time, k, class_loader);

#ifdef ASSERT
  {
    ClassLoaderData* loader_data = k->class_loader_data();
    MutexLocker mu(SystemDictionary_lock, THREAD);
    Klass* kk = find_class(name, loader_data);
    assert(kk == k(), "should be present in dictionary");
  }
#endif

  // return if the protection domain in NULL
  if (protection_domain() == NULL) return k();

  // Check the protection domain has the right access
  {
    MutexLocker mu(SystemDictionary_lock, THREAD);
    // Note that we have an entry, and entries can be deleted only during GC,
    // so we cannot allow GC to occur while we're holding this entry.
    // We're using a No_Safepoint_Verifier to catch any place where we
    // might potentially do a GC at all.
    // Dictionary::do_unloading() asserts that classes in SD are only
    // unloaded at a safepoint. Anonymous classes are not in SD.
    No_Safepoint_Verifier nosafepoint;
    if (dictionary()->is_valid_protection_domain(d_index, d_hash, name,
                                                 loader_data,
                                                 protection_domain)) {
      return k();
    }
  }

  // Verify protection domain. If it fails an exception is thrown
  validate_protection_domain(k, class_loader, protection_domain, CHECK_NULL);

  return k();
}

该方法特别长,而且涉及父类加载以及类的并行加载等机制,但本文重点关注该函数调用到的load_instance_class方法。

启动类加载器加载类

cpp
hotspot/src/share/vm/classfile/systemDictionary.cpp
instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {
  instanceKlassHandle nh = instanceKlassHandle(); // null Handle
  if (class_loader.is_null()) { // 使用引导类加载器加载类

    // Search the shared system dictionary for classes preloaded into the
    // shared spaces.
    instanceKlassHandle k;
    {
      PerfTraceTime vmtimer(ClassLoader::perf_shared_classload_time());
      // 在共享字典中搜索预加载的类,默认不使用共享空间,因此查找的结果为NULL
      k = load_shared_class(class_name, class_loader, THREAD);
    }

    // 如果没有找到类
    if (k.is_null()) {
      // Use VM class loader
      PerfTraceTime vmtimer(ClassLoader::perf_sys_classload_time());
      /*
       * 使用引导类加载器加载类
       */
      k = ClassLoader::load_classfile(class_name, CHECK_(nh));
    }

    // find_or_define_instance_class may return a different InstanceKlass
    // 如果找到了类
    if (!k.is_null()) {
    /*
     * 将生成的Klass对象缓存起来
     */
      k = find_or_define_instance_class(class_name, class_loader, k, CHECK_(nh));
    }
    return k;
  } else { // 使用指定的类加载器加载,最终会调用java.lang.ClassLoader类中的loadClass()方法执行类加载
    // Use user specified class loader to load class. Call loadClass operation on class_loader.
    ResourceMark rm(THREAD);

    assert(THREAD->is_Java_thread(), "must be a JavaThread");
    JavaThread* jt = (JavaThread*) THREAD;

    PerfClassTraceTime vmtimer(ClassLoader::perf_app_classload_time(),
                               ClassLoader::perf_app_classload_selftime(),
                               ClassLoader::perf_app_classload_count(),
                               jt->get_thread_stat()->perf_recursion_counts_addr(),
                               jt->get_thread_stat()->perf_timers_addr(),
                               PerfClassTraceTime::CLASS_LOAD);

    Handle s = java_lang_String::create_from_symbol(class_name, CHECK_(nh));
    // Translate to external class name format, i.e., convert '/' chars to '.'
    Handle string = java_lang_String::externalize_classname(s, CHECK_(nh));

    JavaValue result(T_OBJECT);

    KlassHandle spec_klass (THREAD, SystemDictionary::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),
    // so the call to loadClassInternal() was not required.
    //
    // UnsyncloadClass flag means both call loadClass(String) and do
    // not acquire the class loader lock even for class loaders that are
    // not parallelCapable. This was a risky transitional
    // flag for diagnostic purposes only. It is risky to call
    // custom class loaders without synchronization.
    // WARNING If a custom class loader does NOT synchronizer findClass, or callers of
    // findClass, the UnsyncloadClass flag risks unexpected timing bugs in the field.
    // Do NOT assume this will be supported in future releases.
    //
    // Added MustCallLoadClassInternal in case we discover in the field
    // a customer that counts on this call
    if (MustCallLoadClassInternal && has_loadClassInternal()) {
      // 调用java.lang.ClassLoader对象中的loadClass()方法进行类加载
      JavaCalls::call_special(&result,
                              class_loader,
                              spec_klass,
                              vmSymbols::loadClassInternal_name(),
                              vmSymbols::string_class_signature(),
                              string,
                              CHECK_(nh));
    } else {
      JavaCalls::call_virtual(&result,
                              class_loader,
                              spec_klass,
                              vmSymbols::loadClass_name(),
                              vmSymbols::string_class_signature(),
                              string,
                              CHECK_(nh));
    }

    assert(result.get_type() == T_OBJECT, "just checking");
    // 获取调用loadClass()方法返回的java.lang.Class对象
    oop obj = (oop) result.get_jobject();

    // Primitive classes return null since forName() can not be
    // used to obtain any of the Class objects representing primitives or void
    // 调用loadClass()方法加载的必须是对象类型
    if ((obj != NULL) && !(java_lang_Class::is_primitive(obj))) {
      /*
       * 获取java.lang.Class对象表示的Java类,也就是获取表示Java类的instanceKlass实例
       * 为什么这里获取到后不将Klass对象缓存起来?
       */
      instanceKlassHandle k =
                instanceKlassHandle(THREAD, 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 nh;
  }
}

重点关注第一个if代码块中的内容,关键的地方在于两点:

  • 调用启动类加载器ClassLoaderload_classfile函数来加载类;
  • 调用find_or_define_instance_class来将加载到的类缓存起来;
cpp
hotspot/src/share/vm/classfile/classLoader.cpp
/*
 * 加载类文件
 */
instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) {
  ResourceMark rm(THREAD);
  EventMark m("loading class %s", h_name->as_C_string());
  ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion);

  // 获取类文件名称
  stringStream st;
  // st.print() uses too much stack space while handling a StackOverflowError
  // st.print("%s.class", h_name->as_utf8());
  st.print_raw(h_name->as_utf8());
  st.print_raw(".class");
  char* name = st.as_string();

  // Lookup stream for parsing .class file
  // 根据文件名称查找class文件
  ClassFileStream* stream = NULL;
  int classpath_index = 0;
  {
    PerfClassTraceTime vmtimer(perf_sys_class_lookup_time(),
                               ((JavaThread*) THREAD)->get_thread_stat()->perf_timers_addr(),
                               PerfClassTraceTime::CLASS_LOAD);
    /*
     * 从第一个classpath开始遍历所有的类路径。
     * 为什么现在的Java应用不用设置classpath,早期版本则需要?
     * 或者说Java应用的classpath是怎么被设置的?
     */
    ClassPathEntry* e = _first_entry;
    while (e != NULL) {
      // 从该类路径下获取类文件
      stream = e->open_stream(name, CHECK_NULL);
      // 如果找到目标类文件,则跳出循环
      if (stream != NULL) {
        break;
      }
      // 获取下一个classpath,所有的ClassPathEntry由单链表连接起的
      e = e->next();
      ++classpath_index;
    }
  }

  // 保存目标类的句柄
  instanceKlassHandle h;
  // 如果找到了文件,则加载并解析
  if (stream != NULL) {

    // class file found, parse it
    ClassFileParser parser(stream);
    // 获取引导类加载器对应的ClassLoaderData实例
    ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
    Handle protection_domain;
    TempNewSymbol parsed_name = NULL;
    // 加载并解析class文件,但暂时不连接
    instanceKlassHandle result = parser.parseClassFile(h_name,
                                                       loader_data,
                                                       protection_domain,
                                                       parsed_name,
                                                       false, // 不验证
                                                       CHECK_(h));

    // add to package table
    // 保存已经解析的类,避免重复加载解析
    if (add_package(name, classpath_index, THREAD)) {
      h = result;
    }
  }

  return h;
}

上面的代码中遍历了ClassPathEntry链表,即从所有能够加载的地方尝试加载,直到加载到目标类。随后调用了parseClassFile函数来解析类文件,这部分篇幅很多,可以单独写很多篇文章了,本文重点关注类加载的逻辑,所以不再深入。

缓存类对象

加载到类后,会将其缓存起来。

cpp
hotspot/src/share/vm/classfile/systemDictionary.cpp
// Update system dictionary - done after check_constraint and add_to_hierachy
// have been called.
void SystemDictionary::update_dictionary(int d_index, unsigned int d_hash,
                                         int p_index, unsigned int p_hash,
                                         instanceKlassHandle k,
                                         Handle class_loader,
                                         TRAPS) {
  // Compile_lock prevents systemDictionary updates during compilations
  assert_locked_or_safepoint(Compile_lock);
  Symbol*  name  = k->name();
  ClassLoaderData *loader_data = class_loader_data(class_loader);

  {
  MutexLocker mu1(SystemDictionary_lock, THREAD);

  // See whether biased locking is enabled and if so set it for this
  // klass.
  // Note that this must be done past the last potential blocking
  // point / safepoint. We enable biased locking lazily using a
  // VM_Operation to iterate the SystemDictionary and installing the
  // biasable mark word into each InstanceKlass's prototype header.
  // To avoid race conditions where we accidentally miss enabling the
  // optimization for one class in the process of being added to the
  // dictionary, we must not safepoint after the test of
  // BiasedLocking::enabled().
  if (UseBiasedLocking && BiasedLocking::enabled()) {
    // Set biased locking bit for all loaded classes; it will be
    // cleared if revocation occurs too often for this type
    // NOTE that we must only do this when the class is initally
    // defined, not each time it is referenced from a new class loader
    if (k->class_loader() == class_loader()) {
      k->set_prototype_header(markOopDesc::biased_locking_prototype());
    }
  }

  // Make a new system dictionary entry.
  Klass* sd_check = find_class(d_index, d_hash, name, loader_data);
  if (sd_check == NULL) {
    // 将Klass对象添加到缓存中
    dictionary()->add_klass(name, loader_data, k);
    notice_modification();
  }
#ifdef ASSERT
  sd_check = find_class(d_index, d_hash, name, loader_data);
  assert (sd_check != NULL, "should have entry in system dictionary");
  // Note: there may be a placeholder entry: for circularity testing
  // or for parallel defines
#endif
    SystemDictionary_lock->notify_all();
  }
}

调用字典对象的add_klass函数来缓存类对象。

cpp
hotspot/src/share/vm/classfile/dictionary.cpp
void Dictionary::add_klass(Symbol* class_name, ClassLoaderData* loader_data,
                           KlassHandle obj) {
  assert_locked_or_safepoint(SystemDictionary_lock);
  assert(obj() != NULL, "adding NULL obj");
  assert(obj()->name() == class_name, "sanity check on name");
  assert(loader_data != NULL, "Must be non-NULL");

  // 计算哈希值
  unsigned int hash = compute_hash(class_name, loader_data);
  // 计算在哈希桶中的索引
  int index = hash_to_index(hash);
  // 创建在哈希表中的项
  DictionaryEntry* entry = new_entry(hash, obj(), loader_data);
  // 添加到哈希表中
  add_entry(index, entry);
}

JVM使用的是一个哈希表结构来保存数据,调用了compute_hash函数来计算hash值。

c
hotspot/src/share/vm/utilities/hashtable.hpp
unsigned int compute_hash(Symbol* name, ClassLoaderData* loader_data) {
  // 计算目标类名的哈希值
  unsigned int name_hash = name->identity_hash();
  // loader is null with CDS
  assert(loader_data != NULL || UseSharedSpaces || DumpSharedSpaces,
         "only allowed with shared spaces");
  // 计算类加载器的哈希值
  unsigned int loader_hash = loader_data == NULL ? 0 : loader_data->identity_hash();
  // 异或两者
  return name_hash ^ loader_hash;
}

可以看到,这里同时哈希了类名和类加载器数据。所以对于同一个类文件,使用不同的类加载器来加载,也算是不同的类。

加载数组类

cpp
hotspot/src/share/vm/classfile/systemDictionary.cpp
Klass* SystemDictionary::resolve_array_class_or_null(Symbol* class_name,
                                                       Handle class_loader,
                                                       Handle protection_domain,
                                                       TRAPS) {
  assert(FieldType::is_array(class_name), "must be array");
  Klass* k = NULL;
  FieldArrayInfo fd;
  // dimension and object_key in FieldArrayInfo are assigned as a side-effect
  // of this call
  // 获取数组元素的类型
  BasicType t = FieldType::get_array_info(class_name, fd, CHECK_NULL);
  if (t == T_OBJECT) { // 数组元素的类型为对象
    // naked oop "k" is OK here -- we assign back into it
    k = SystemDictionary::resolve_instance_class_or_null(fd.object_key(),
                                                         class_loader,
                                                         protection_domain,
                                                         CHECK_NULL);
    if (k != NULL) {
      k = k->array_klass(fd.dimension(), CHECK_NULL);
    }
  } else { // 数组元素为Java基本类型
    k = Universe::typeArrayKlassObj(t);
    k = TypeArrayKlass::cast(k)->array_klass(fd.dimension(), CHECK_NULL);
  }
  return k;
}

这里又分为两种情况:

  • 数组元素是Java基本类型:这里首先是基本类型,然后调用其array_klass函数来获取对应的数组类型。
  • 数组元素是引用类型:仍然和加载普通类一样调用resolve_instance_class_or_null函数来加载数组元素的类,上面已经走过一遍代码流程了,不再赘述。然后调用其array_klass函数来获取其数组类型。

具体的数组类型获取过程本文省略。

普通类加载器加载类

JDK层面

java
jdk/src/share/classes/java/lang/ClassLoader.java
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

子类必须实现该方法,应用类加载器和扩展类加载器都继承了URLClassLoader,而该类刚好重写了该方法。

java
jdk/src/share/classes/java/net/URLClassLoader.java
protected Class<?> findClass(final String name)
     throws ClassNotFoundException
{
    try {
        return AccessController.doPrivileged(
            new PrivilegedExceptionAction<Class<?>>() {
                public Class<?> run() throws ClassNotFoundException {
                    // 获取到类文件
                    String path = name.replace('.', '/').concat(".class");
                    Resource res = ucp.getResource(path, false);
                    if (res != null) {
                        try {
                            // 处理类文件
                            return defineClass(name, res);
                        } catch (IOException e) {
                            throw new ClassNotFoundException(name, e);
                        }
                    } else {
                        throw new ClassNotFoundException(name);
                    }
                }
            }, acc);
    } catch (java.security.PrivilegedActionException pae) {
        throw (ClassNotFoundException) pae.getException();
    }
}

关键的地方是调用了defineClass方法,接下来会介绍很多和此方法同名的方法,这些方法位于父类中。

java
jdk/src/share/classes/java/net/URLClassLoader.java
private Class<?> defineClass(String name, Resource res) throws IOException {
    long t0 = System.nanoTime();
    int i = name.lastIndexOf('.');
    URL url = res.getCodeSourceURL();
    if (i != -1) {
        // 获取类的全限定名称,截取.class后缀前面的部分
        String pkgname = name.substring(0, i);
        // Check if package already loaded.
        Manifest man = res.getManifest();
        // 获取并验证包
        if (getAndVerifyPackage(pkgname, man, url) == null) {
            try {
                if (man != null) {
                    definePackage(pkgname, man, url);
                } else {
                    definePackage(pkgname, null, null, null, null, null, null, null);
                }
            } catch (IllegalArgumentException iae) {
                // parallel-capable class loaders: re-verify in case of a
                // race condition
                if (getAndVerifyPackage(pkgname, man, url) == null) {
                    // Should never happen
                    throw new AssertionError("Cannot find package " +
                                             pkgname);
                }
            }
        }
    }
    // Now read the class bytes and define the class
    java.nio.ByteBuffer bb = res.getByteBuffer();
    if (bb != null) {
        // Use (direct) ByteBuffer:
        CodeSigner[] signers = res.getCodeSigners();
        CodeSource cs = new CodeSource(url, signers);
        sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
        // 加载类
        return defineClass(name, bb, cs);
    } else {
        byte[] b = res.getBytes();
        // must read certificates AFTER reading bytes.
        CodeSigner[] signers = res.getCodeSigners();
        CodeSource cs = new CodeSource(url, signers);
        sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
        // 加载类
        return defineClass(name, b, 0, b.length, cs);
    }
}

这里调用的两个defineClass方法都位于父类SecureClassLoader中。

java
jdk/src/share/classes/java/security/SecureClassLoader.java
protected final Class<?> defineClass(String name,
                                     byte[] b, int off, int len,
                                     CodeSource cs)
{
    return defineClass(name, b, off, len, getProtectionDomain(cs));
}
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
                                     CodeSource cs)
{
    // 调用父类ClassLoader的方法
    return defineClass(name, b, getProtectionDomain(cs));
}

继续调用父类ClassLoader中的方法。

java
jdk/src/share/classes/java/lang/ClassLoader.java
/*
 * 该类中的defineClass实现都是final的,不能被重写。
 */
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                     ProtectionDomain protectionDomain)
    throws ClassFormatError
{
    // 类加载前置处理
    protectionDomain = preDefineClass(name, protectionDomain);
    String source = defineClassSourceLocation(protectionDomain);
    // 加载类
    Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
    // 类加载后置逻辑
    postDefineClass(c, protectionDomain);
    return c;
}
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
                                     ProtectionDomain protectionDomain)
    throws ClassFormatError
{
    int len = b.remaining();

    // Use byte[] if not a direct ByteBufer:
    if (!b.isDirect()) {
        if (b.hasArray()) {
            // 调用上面的重载方法
            return defineClass(name, b.array(),
                               b.position() + b.arrayOffset(), len,
                               protectionDomain);
        } else {
            // no array, or read-only array
            byte[] tb = new byte[len];
            b.get(tb);  // get bytes out of byte buffer.
            // 通过Hotspot VM来加载类
            return defineClass(name, tb, 0, len, protectionDomain);
        }
    }

    // 做一些前置操作
    protectionDomain = preDefineClass(name, protectionDomain);
    // 获取类来源位置
    String source = defineClassSourceLocation(protectionDomain);
    Class<?> c = defineClass2(name, b, b.position(), len, protectionDomain, source);
    // 后置操作
    postDefineClass(c, protectionDomain);
    return c;
}
private native Class<?> defineClass0(String name, byte[] b, int off, int len,
                                     ProtectionDomain pd);

private native Class<?> defineClass1(String name, byte[] b, int off, int len,
                                     ProtectionDomain pd, String source);

private native Class<?> defineClass2(String name, java.nio.ByteBuffer b,
                                     int off, int len, ProtectionDomain pd,
                                     String source);

最后会调用到几个本地方法来让JVM加载类。 这里补充一个问题:为什么我们不能加载我们自定义的以java.开头的类呢?在上面调用本地方法之前,会进行类加载的前置操作。

java
jdk/src/share/classes/java/lang/ClassLoader.java
private ProtectionDomain preDefineClass(String name,
                                        ProtectionDomain pd)
{
    if (!checkName(name))
        throw new NoClassDefFoundError("IllegalName: " + name);

    /*
     * 从这里可以看出,加载的类的全限定名称不能以java开头
     * 《深入理解Java虚拟机(第三版)》的P284页脚注提到的异常就是这里抛出的。
     */
    if ((name != null) && name.startsWith("java.")) {
        throw new SecurityException
            ("Prohibited package name: " +
             name.substring(0, name.lastIndexOf('.')));
    }
    if (pd == null) {
        pd = defaultDomain;
    }

    if (name != null) checkCerts(name, pd.getCodeSource());

    return pd;
}

在该方法中,检查了目标类的全限定名是否是以java.开头的,如果是则会抛出异常。以java.开头的类都是Java的核心类,为了安全只能由启动类加载器来加载,Java代码实现的其他类加载器不能加载。

JVM层面

defineClass0
defineClass1
defineClass2
<
>
c
jdk/src/share/native/java/lang/ClassLoader.c
// The existence or signature of this method is not guaranteed since it
// supports a private method.  This method will be changed in 1.7.
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_defineClass0(JNIEnv *env,
                                        jobject loader,
                                        jstring name,
                                        jbyteArray data,
                                        jint offset,
                                        jint length,
                                        jobject pd)
{
    return Java_java_lang_ClassLoader_defineClass1(env, loader, name, data, offset,
                                                   length, pd, NULL);
}
c
jdk/src/share/native/java/lang/ClassLoader.c
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_defineClass1(JNIEnv *env,
                                        jobject loader,
                                        jstring name,
                                        jbyteArray data,
                                        jint offset,
                                        jint length,
                                        jobject pd,
                                        jstring source)
{
    jbyte *body;
    char *utfName;
    jclass result = 0;
    char buf[128];
    char* utfSource;
    char sourceBuf[1024];

    if (data == NULL) {
        JNU_ThrowNullPointerException(env, 0);
        return 0;
    }

    /* Work around 4153825. malloc crashes on Solaris when passed a
     * negative size.
     */
    if (length < 0) {
        JNU_ThrowArrayIndexOutOfBoundsException(env, 0);
        return 0;
    }

    body = (jbyte *)malloc(length);

    if (body == 0) {
        JNU_ThrowOutOfMemoryError(env, 0);
        return 0;
    }

    (*env)->GetByteArrayRegion(env, data, offset, length, body);

    if ((*env)->ExceptionOccurred(env))
        goto free_body;

    if (name != NULL) {
        utfName = getUTF(env, name, buf, sizeof(buf));
        if (utfName == NULL) {
            JNU_ThrowOutOfMemoryError(env, NULL);
            goto free_body;
        }
        VerifyFixClassname(utfName);
    } else {
        utfName = NULL;
    }

    if (source != NULL) {
        utfSource = getUTF(env, source, sourceBuf, sizeof(sourceBuf));
        if (utfSource == NULL) {
            JNU_ThrowOutOfMemoryError(env, NULL);
            goto free_utfName;
        }
    } else {
        utfSource = NULL;
    }
    /*
     * 加载类
     */
    result = JVM_DefineClassWithSource(env, utfName, loader, body, length, pd, utfSource);

    if (utfSource && utfSource != sourceBuf)
        free(utfSource);

 free_utfName:
    if (utfName && utfName != buf)
        free(utfName);

 free_body:
    free(body);
    return result;
}
c
jdk/src/share/native/java/lang/ClassLoader.c
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_defineClass2(JNIEnv *env,
                                        jobject loader,
                                        jstring name,
                                        jobject data,
                                        jint offset,
                                        jint length,
                                        jobject pd,
                                        jstring source)
{
    jbyte *body;
    char *utfName;
    jclass result = 0;
    char buf[128];
    char* utfSource;
    char sourceBuf[1024];

    assert(data != NULL); // caller fails if data is null.
    assert(length >= 0);  // caller passes ByteBuffer.remaining() for length, so never neg.
    // caller passes ByteBuffer.position() for offset, and capacity() >= position() + remaining()
    assert((*env)->GetDirectBufferCapacity(env, data) >= (offset + length));

    body = (*env)->GetDirectBufferAddress(env, data);

    if (body == 0) {
        JNU_ThrowNullPointerException(env, 0);
        return 0;
    }

    body += offset;

    if (name != NULL) {
        utfName = getUTF(env, name, buf, sizeof(buf));
        if (utfName == NULL) {
            JNU_ThrowOutOfMemoryError(env, NULL);
            return result;
        }
        VerifyFixClassname(utfName);
    } else {
        utfName = NULL;
    }

    if (source != NULL) {
        utfSource = getUTF(env, source, sourceBuf, sizeof(sourceBuf));
        if (utfSource == NULL) {
            JNU_ThrowOutOfMemoryError(env, NULL);
            goto free_utfName;
        }
    } else {
        utfSource = NULL;
    }
    // 加载类
    result = JVM_DefineClassWithSource(env, utfName, loader, body, length, pd, utfSource);

    if (utfSource && utfSource != sourceBuf)
        free(utfSource);

 free_utfName:
    if (utfName && utfName != buf)
        free(utfName);

    return result;
}

三个函数最终都会调用到JVM_DefineClassWithSource函数。

cpp
hotspot/src/share/vm/prims/jvm.cpp
JVM_ENTRY(jclass, JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source))
  JVMWrapper2("JVM_DefineClassWithSource %s", name);

  return jvm_define_class_common(env, name, loader, buf, len, pd, source, true, THREAD);
JVM_END
// common code for JVM_DefineClass() and JVM_DefineClassWithSource()
// and JVM_DefineClassWithSourceCond()
static jclass jvm_define_class_common(JNIEnv *env, const char *name,
                                      jobject loader, const jbyte *buf,
                                      jsize len, jobject pd, const char *source,
                                      jboolean verify, TRAPS) {
  if (source == NULL)  source = "__JVM_DefineClass__";

  assert(THREAD->is_Java_thread(), "must be a JavaThread");
  JavaThread* jt = (JavaThread*) THREAD;

  PerfClassTraceTime vmtimer(ClassLoader::perf_define_appclass_time(),
                             ClassLoader::perf_define_appclass_selftime(),
                             ClassLoader::perf_define_appclasses(),
                             jt->get_thread_stat()->perf_recursion_counts_addr(),
                             jt->get_thread_stat()->perf_timers_addr(),
                             PerfClassTraceTime::DEFINE_CLASS);

  if (UsePerfData) {
    ClassLoader::perf_app_classfile_bytes_read()->inc(len);
  }

  // Since exceptions can be thrown, class initialization can take place
  // if name is NULL no check for class name in .class stream has to be made.
  TempNewSymbol class_name = NULL;
  if (name != NULL) {
    const int str_len = (int)strlen(name);
    if (str_len > Symbol::max_length()) {
      // It's impossible to create this class;  the name cannot fit
      // into the constant pool.
      THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name);
    }
    class_name = SymbolTable::new_symbol(name, str_len, CHECK_NULL);
  }

  ResourceMark rm(THREAD);
  // 准备将类文件转换为文件流
  ClassFileStream st((u1*) buf, len, (char *)source);
  Handle class_loader (THREAD, JNIHandles::resolve(loader));
  if (UsePerfData) {
    is_lock_held_by_thread(class_loader,
                           ClassLoader::sync_JVMDefineClassLockFreeCounter(),
                           THREAD);
  }
  Handle protection_domain (THREAD, JNIHandles::resolve(pd));
  /*
   * 从文件流中解析生成Klass实例
   */
  Klass* k = SystemDictionary::resolve_from_stream(class_name, class_loader,
                                                     protection_domain, &st,
                                                     verify != 0,
                                                     CHECK_NULL);

  if (TraceClassResolution && k != NULL) {
    trace_class_resolution(k);
  }

  // 将Klass对象转为java.lang.Class对象
  return (jclass) JNIHandles::make_local(env, k->java_mirror());
}

继续看resolve_from_stream函数。

cpp
hotspot/src/share/vm/classfile/systemDictionary.cpp
Klass* SystemDictionary::resolve_from_stream(Symbol* class_name,
                                             Handle class_loader,
                                             Handle protection_domain,
                                             ClassFileStream* st,
                                             bool verify,
                                             TRAPS) {

  // Classloaders that support parallelism, e.g. bootstrap classloader,
  // or all classloaders with UnsyncloadClass do not acquire lock here
  bool DoObjectLock = true;
  if (is_parallelCapable(class_loader)) {
    DoObjectLock = false;
  }

  ClassLoaderData* loader_data = register_loader(class_loader, CHECK_NULL);

  // Make sure we are synchronized on the class loader before we proceed
  Handle lockObject = compute_loader_lock_object(class_loader, THREAD);
  check_loader_lock_contention(lockObject, THREAD);
  ObjectLocker ol(lockObject, THREAD, DoObjectLock);

  TempNewSymbol parsed_name = NULL;

  // Parse the stream. Note that we do this even though this klass might
  // already be present in the SystemDictionary, otherwise we would not
  // throw potential ClassFormatErrors.
  //
  // Note: "name" is updated.

  /*
   * 解析类文件并生成Klass实例
   */
  instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name,
                                                             loader_data,
                                                             protection_domain,
                                                             parsed_name,
                                                             verify,
                                                             THREAD);

  const char* pkg = "java/";
  if (!HAS_PENDING_EXCEPTION &&
      !class_loader.is_null() &&
      parsed_name != NULL &&
      /*
       * 这里检查了加载的类的全限定名称不能以java开头,否则报错。
       * 其实在JDK类ClassLoader中的preDefineClass方法中就已经检查过了,这里再检查一下。
       * 这里的报错信息和JDK类中的报错一样,也是《深入理解Java虚拟机(第三版)》中P284脚注中提到的报错。
       *
       * 这里有点绕,要使if条件为true,那么strncmp函数返回值就必须为0,也就是类名称以java开头。
       */
      !strncmp((const char*)parsed_name->bytes(), pkg, strlen(pkg))) {
    // It is illegal to define classes in the "java." package from
    // JVM_DefineClass or jni_DefineClass unless you're the bootclassloader
    ResourceMark rm(THREAD);
    char* name = parsed_name->as_C_string();
    char* index = strrchr(name, '/');
    *index = '\0'; // chop to just the package name
    while ((index = strchr(name, '/')) != NULL) {
      *index = '.'; // replace '/' with '.' in package name
    }
    const char* fmt = "Prohibited package name: %s";
    size_t len = strlen(fmt) + strlen(name);
    char* message = NEW_RESOURCE_ARRAY(char, len);
    jio_snprintf(message, len, fmt, name);
    // 抛出异常
    Exceptions::_throw_msg(THREAD_AND_LOCATION,
      vmSymbols::java_lang_SecurityException(), message);
  }

  if (!HAS_PENDING_EXCEPTION) {
    assert(parsed_name != NULL, "Sanity");
    assert(class_name == NULL || class_name == parsed_name, "name mismatch");
    // Verification prevents us from creating names with dots in them, this
    // asserts that that's the case.
    assert(is_internal_format(parsed_name),
           "external class name format used internally");

    // Add class just loaded
    // If a class loader supports parallel classloading handle parallel define requests
    // find_or_define_instance_class may return a different InstanceKlass
    if (is_parallelCapable(class_loader)) { // 支持并行加载
      k = find_or_define_instance_class(class_name, class_loader, k, THREAD);
    } else { // 如果不能并行加载,那么直接将Klass实例注册到SystemDictionary中
      define_instance_class(k, THREAD);
    }
  }

  // Make sure we have an entry in the SystemDictionary on success
  debug_only( {
    if (!HAS_PENDING_EXCEPTION) {
      assert(parsed_name != NULL, "parsed_name is still null?");
      Symbol*  h_name    = k->name();
      ClassLoaderData *defining_loader_data = k->class_loader_data();

      MutexLocker mu(SystemDictionary_lock, THREAD);

      Klass* check = find_class(parsed_name, loader_data);
      assert(check == k(), "should be present in the dictionary");

      Klass* check2 = find_class(h_name, defining_loader_data);
      assert(check == check2, "name inconsistancy in SystemDictionary");
    }
  } );

  return k();
}

调用了ClassFileParser类的parseClassFile函数来解析类文件,上面介绍启动类加载器加载类时也走到了这个函数。该函数比较复杂,具体的代码实现超过了500行,包含了类文件格式的解析等操作,后面单独写文章来分析。 在上面resolve_from_stream函数中,解析完类文件之后,再次判断了类全限定名是否以java.开头,如果是则抛出异常,因为只有启动类加载器才能加载以java.开头的类。从而做到Java核心类在JDK和JVM层的安全加载。

判断类是否被加载过

这里说的是判断类是否被当前类加载器加载过,即在ClassLoader类的loadClass方法中调用的findLoadedClass方法。

java
jdk/src/share/classes/java/lang/ClassLoader.java
protected final Class<?> findLoadedClass(String name) {
    if (!checkName(name))
        return null;
    return findLoadedClass0(name);
}
private native final Class<?> findLoadedClass0(String name);

同样地,显示检查了类名,然后调用本地方法。

c
jdk/src/share/native/java/lang/ClassLoader.c
/*
 * 注意这里确实是这里的三个参数和Java类中方法的一个参数不匹配,至于为什么要参考JNI的实现机制。
 */
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_findLoadedClass0(JNIEnv *env, jobject loader,
                                           jstring name)
{
    if (name == NULL) {
        return 0;
    } else {
        // 查找类
        return JVM_FindLoadedClass(env, loader, name);
    }
}

然后调用JVM_FindLoadedClass函数进入Hotspot VM的实现。

cpp
hotspot/src/share/vm/prims/jvm.cpp
JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name))
  JVMWrapper("JVM_FindLoadedClass");
  ResourceMark rm(THREAD);

  Handle h_name (THREAD, JNIHandles::resolve_non_null(name));
  // 获取类名对应的handle
  Handle string = java_lang_String::internalize_classname(h_name, CHECK_NULL);

  const char* str   = java_lang_String::as_utf8_string(string());
  // Sanity check, don't expect null
  // 检查类名是否为空
  if (str == NULL) return NULL;

  // 判断类名是否过长
  const int str_len = (int)strlen(str);
  if (str_len > Symbol::max_length()) {
    // It's impossible to create this class;  the name cannot fit
    // into the constant pool.
    return NULL;
  }
  // 创建一个临时的symbol实例
  TempNewSymbol klass_name = SymbolTable::new_symbol(str, str_len, CHECK_NULL);

  // Security Note:
  //   The Java level wrapper will perform the necessary security check allowing
  //   us to pass the NULL as the initiating class loader.
  // 获取类加载器对应的handle
  Handle h_loader(THREAD, JNIHandles::resolve(loader));
  if (UsePerfData) {
    is_lock_held_by_thread(h_loader,
                           ClassLoader::sync_JVMFindLoadedClassLockFreeCounter(),
                           THREAD);
  }

  /*
   * 查找目标类是否存在,Hotspot将所有加载的类保存在哈希表中(采用链地址法来解决冲突)
   */
  Klass* k = SystemDictionary::find_instance_or_array_klass(klass_name,
                                                              h_loader,
                                                              Handle(),
                                                              CHECK_NULL);

  // 将Klass实例转换成java.lang.Class对象
  return (k == NULL) ? NULL :
            (jclass) JNIHandles::make_local(env, k->java_mirror());
JVM_END

跟进find_instance_or_array_klass函数。

cpp
hotspot/src/share/vm/classfile/systemDictionary.cpp
// Look for a loaded instance or array klass by name.  Do not do any loading.
// return NULL in case of error.
// 查找InstanceKlass或ArrayKlass实例
Klass* SystemDictionary::find_instance_or_array_klass(Symbol* class_name,
                                                      Handle class_loader,
                                                      Handle protection_domain,
                                                      TRAPS) {
  Klass* k = NULL;
  assert(class_name != NULL, "class name must be non NULL");

  if (FieldType::is_array(class_name)) { // 数组的查找逻辑
    // The name refers to an array.  Parse the name.
    // dimension and object_key in FieldArrayInfo are assigned as a
    // side-effect of this call
    FieldArrayInfo fd;
    // 获取数组的元素类型
    BasicType t = FieldType::get_array_info(class_name, fd, CHECK_(NULL));
    if (t != T_OBJECT) { // 元素类型为Java基本类型
      k = Universe::typeArrayKlassObj(t);
    } else { // 元素类型为Java对象
      k = SystemDictionary::find(fd.object_key(), class_loader, protection_domain, THREAD);
    }
    if (k != NULL) {
      // 查找或创建相应维度的ObjArrayKlass实例
      k = k->array_klass_or_null(fd.dimension());
    }
  } else { // 类的查找逻辑
    k = find(class_name, class_loader, protection_domain, THREAD);
  }
  return k;
}

在该函数中,分为是数组还是普通类型两种情况。查找数组类的情况,如果元素是基本类型,那么毫无疑问肯定存在对应类;如果元素是引用类型,主要看该引用类型本身是否存在,所以还是回到了下面的find函数。

查找普通类

cpp
hotspot/src/share/vm/classfile/systemDictionary.cpp
Klass* SystemDictionary::find(Symbol* class_name,
                              Handle class_loader,
                              Handle protection_domain,
                              TRAPS) {

  // UseNewReflection
  // The result of this call should be consistent with the result
  // of the call to resolve_instance_class_or_null().
  // See evaluation 6790209 and 4474172 for more details.
  class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
  ClassLoaderData* loader_data = ClassLoaderData::class_loader_data_or_null(class_loader());

  if (loader_data == NULL) {
    // If the ClassLoaderData has not been setup,
    // then the class loader has no entries in the dictionary.
    return NULL;
  }

  // 通过类名和类加载器进行哈希运算
  unsigned int d_hash = dictionary()->compute_hash(class_name, loader_data);
  // 根据哈希运算的结果进一步计算得到索引,即在哈希表中的下标
  int d_index = dictionary()->hash_to_index(d_hash);

  {
    // Note that we have an entry, and entries can be deleted only during GC,
    // so we cannot allow GC to occur while we're holding this entry.
    // We're using a No_Safepoint_Verifier to catch any place where we
    // might potentially do a GC at all.
    // Dictionary::do_unloading() asserts that classes in SD are only
    // unloaded at a safepoint. Anonymous classes are not in SD.
    No_Safepoint_Verifier nosafepoint;
    // 根据索引进行查找
    return dictionary()->find(d_index, d_hash, class_name, loader_data,
                              protection_domain, THREAD);
  }
}

有了上面的分析过缓存类的经验,这里逻辑就很简单了,就是根据类名和类加载器数据计算哈希,然后到哈希表中去找。