类加载、链接和初始化 C++源码解析

类加载器种类

  • BootstrapClassLoader:C++ 编写,负责加载 java 核心类库
  • Launcher.ExtClassLoader:Launcher 中的内部类,parent == null
  • Launcher.AppClassLoader:Launcher 中的内部类,parent == ExtClassLoader
  • 用户自定义 ClassLoader:继承自 ClassLoader,parent == AppClassLoader


    image.png

类加载机制

java 中默认的类加载机制是双亲委派模式。

ClassLoader 中关键的方法说明:

loadClass // 类加载入口,包含下面这些步骤

=> findLoadedClass => findLoadedClass0 // 先从缓存中查询一下,看看目标类是否已加载过

=> findBootstrapClassOrNull => findBootstrapClass // 用Bootstrap类加载器进行加载

=> findClass // 读取字节码文件,然后加载字节码文件

    => defineClass // 加载字节码文件

        => preDefineClass // 加载前的检查

        => defineClassSourceLocation // 定义类加载的路径

        => defineClass1/defineClass2 // 调用native方法加载类

        => postDefineClass // 

=> resolveClass => resolveClass0

ClassLoader 部分源码:

package java.lang;

import java.io.InputStream;
...

public abstract class ClassLoader {

    private final ClassLoader parent;

    // -- Class --
    protected Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);  // 缓存机制
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // 双亲委派机制
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        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();
                    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;
        }
    }

    // findClass由子类去实现
    protected Class findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

    // defineClass 加载类
    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;
    }

    private ProtectionDomain preDefineClass(String name,
                                            ProtectionDomain pd)
    {
        if (!checkName(name))
            throw new NoClassDefFoundError("IllegalName: " + name);

        // Note:  Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
        // relies on the fact that spoofing is impossible if a class has a name
        // of the form "java.*"
        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;
    }

    private String defineClassSourceLocation(ProtectionDomain pd)
    {
        CodeSource cs = pd.getCodeSource();
        String source = null;
        if (cs != null && cs.getLocation() != null) {
            source = cs.getLocation().toString();
        }
        return source;
    }

    private void postDefineClass(Class c, ProtectionDomain pd)
    {
        if (pd.getCodeSource() != null) {
            Certificate certs[] = pd.getCodeSource().getCertificates();
            if (certs != null)
                setSigners(c, certs);
        }
    }

    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);

    protected final void resolveClass(Class c) {
        resolveClass0(c);
    }

    private native void resolveClass0(Class c);

    private Class findBootstrapClassOrNull(String name) {
        if (!checkName(name)) return null;

        return findBootstrapClass(name);
    }

    // return null if not found
    private native Class findBootstrapClass(String name);

    protected final Class findLoadedClass(String name) {
        if (!checkName(name))
            return null;
        return findLoadedClass0(name);
    }

findLoadedClass0

JVM已经加载的类中查找目标类由本地方法findLoadedClass0实现,通过启动类加载器加载类由本地方法findLoadedClass0实现,将已经加载完成的class解析并完成链接由本地方法findLoadedClass0实现,将读取class文件的字节数组转换成Class由defineClass0,defineClass1,defineClass2等方法完成,这四个本地方法的底层逻辑是什么样的?神秘的启动类加载器是如何加载rt.jar的?符号引用的解析是如何完成的?类的链接和初始化是怎样实现的?

Symbol

Symbol类的定义位于oops/symbol.hpp中,Symbol表示一个规范化的字符串形式的描述符,如方法Object m(int i, double d, Thread t) {...}对应的方法描述符就是(IDLjava/lang/Thread;)Ljava/lang/Object,参考《Hotspot class文件和字节码解析》。所有的Symbol实例通过保存在全局的SymbolTable中,SymbolTable即符号表,基于此实现符号引用计数功能。当有一个新的指针指向该Symbol实例,则引用计数加1,当该指针销毁时需要将引用计数减1,当一个Symbol的引用计数为0,垃圾回收器就会从SymbolTable中删除该Symbol并回收内存。

其类继承关系如下图:

image.png

SymbolBase定义了三个Symbol的基础属性:

  • _refcount:支持原子操作的short变量,表示该Symbol的引用计数
  • _length:UTF-8字符串的长度
  • _identity_hash:hash唯一标识码

Symbol增加一个字节数组的属性_body,用于存储描述符对应的字符串,定义了可以操作该属性的byte_at_put(int index, int value)方法,打印具体字符串内容的as_C_string(),as_utf8()方法,以及引用计数相关的refcount(),increment_refcount(),decrement_refcount()方法。

SymbolTable和StringTable

SymbolTable和StringTable类的定义位于classfile/symbolTable.hpp中,对应于C/C++编译过程的符号表,用于保存管理所有的Symbol实例,StringTable就是Java特有的字符串常量池。其类继承关系如下:

image.png

即SymbolTable和StringTable实际是一个支持自动扩容的HashMap。

SymbolTable定义了如下五个静态属性:

  • _the_table:SymbolTable指针,即全局实际保存Symbol实例的地方
  • _needs_rehashing:bool变量,是否需要重新hash
  • _symbols_removed:int变量,已经被移除的Symbol的数量
  • _symbols_counted:int变量,当前的Symbol的属性
  • _arena:Arena类指针,表示从未被加载过的描述符

SymbolTable定义的方法都是静态方法,主要有两大类:

  • Symbol创建,释放和查找相关的,如allocate_symbol,new_symbols,new_symbol,release,lookup,probe等方法

  • HashTable自身创建复制相关的,如create_table,copy_buckets,copy_table,rehash_table等。
    StringTable定义了三个静态属性,

  • _the_table:StringTable指针,全局实际保存字符串的地方

  • _needs_rehashing:bool变量,是否需要重新hash

  • _parallel_claimed_idx:volatile int变量,并发标记时使用
    StringTable也重写了HashTable自身创建复制相关的方法,除此之外增加了用于往常量池添加字符串的intern方法,查找字符串的lookup方法。

以根据描述符字符串查找对应Symbol实例的lookup方法为例说明其逻辑:

Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) {
  //计算hash值
  unsigned int hashValue = hash_symbol(name, len);
  //计算hash槽的位置
  int index = the_table()->hash_to_index(hashValue);
  //去指定位置的hash槽中查找是否存在目标Symbol
  Symbol* s = the_table()->lookup(index, name, len, hashValue);
  //如果存在则返回
  if (s != NULL) return s;
  //如果不存在则需要获取锁SymbolTable_lock,并创建一个新的
  MutexLocker ml(SymbolTable_lock, THREAD);
  //会调用allocate_symbol在内存中创建一个新的Symbol,然后以此构建一个新的HashtableEntry实例,将其添加到HashTable中
  return the_table()->basic_add(index, (u1*)name, len, hashValue, true, CHECK_NULL);
}

即符号表中的符号都只是原始字符串形式的描述符,并未涉及符号解析以及解析结果的保存。

ConstantPool

ConstantPool类的定义位于oops/constantPool.hpp文件中,用于表示class文件中的常量池,每个Klass都有对应的ConstantPool,两者是一一对应的。常量池的数据大部分是在class文件解析的时候写入的,可以安全访问;但是CONSTANT_Class_info类型的常量数据是在这个类被解析时修改,这时只能通过解析状态判断这条数据是否修改完成。其类继承关系如下:

image.png

常量池的每项数据都通过类CPSlot表示,其定义跟ConstantPool类位于同一个文件中,只有一个属性,解析结果Klass或者Symbol的地址,如果未解析则地址是0,可以将该地址转换成Klass或者Symbol类的指针,定义了对应的转换方法(get_symbol和get_klass方法)和判断该数据项是否已经解析的方法(is_resolved和is_unresolved方法)。

ConstantPool定义如下关键属性:

  • _tags:单字节数组指针,描述常量池所有数据的类型的tag数组,每个tag用一个单字节表示

  • _cache:ConstantPoolCache类指针,保存解释器运行时用到的动态调用相关信息的缓存

  • _pool_holder:InstanceKlass指针,当前常量池所属的Klass实例

  • _operands:两字节的数组指针,为大小可变的常量池数据项使用,通常为空

  • _resolved_references:jobject类型,实际是_jobject指针的别名,_jobject等同于C++层面的Java Object对象,表示已经解析的对象数组

  • _reference_map:两字节的数组指针,表示已经解析的对象的索引到原始的常量池的索引的映射关系
    ConstantPool定义了读取和解析常量池数据的诸多方法,大致有以下几类:

  • 属性读写的方法,如tags,set_tags,flags,set_flags,pool_holder,set_pool_holder,cache,set_cache等方法

  • 读取基地址指定偏移位置的值的方法,如obj_at_addr_raw,long_at_addr,double_at_addr等方法

  • 向常量池指定位置写入数据的方法,解析class文件时调用该类方法,如klass_at_put,unresolved_klass_at_put,method_handle_index_at_put,invoke_dynamic_at_put,int_at_put,field_at_put,name_and_type_at_put等方法

  • 从常量池指定位置读取数据的方法,如klass_at,klass_name_at,resolved_klass_at,long_at,symbol_at,name_and_type_at,method_handle_name_ref_at等方法

  • 解析常量池符号引用的方法,如resolve_constant_at,resolve_bootstrap_specifier_at,resolve_constant_at_impl,resolve_string_constants_impl,resolve_bootstrap_specifier_at_impl等方法

  • 从class文件读取常量池的字节流时校验常量池是否符合规范的方法,如verify_on

至此答案已经明确,符号引用的解析,解析结果的缓存都由ConstantPool完成,以klass_at_impl(constantPoolHandle this_oop, int which, TRAPS)方法的实现为例说明,如下:

Klass* ConstantPool::klass_at_impl(constantPoolHandle this_oop, int which, TRAPS) {
  //获取指定位置的数据项,判断其是否已解析
  CPSlot entry = this_oop->slot_at(which);
  if (entry.is_resolved()) {
    //如果已解析但是不是Klass类型则抛出异常
    assert(entry.get_klass()->is_klass(), "must be");
    //返回解析结果
    return entry.get_klass();
  }
 
  //判断调用方线程是否是Java线程,不能是本地的C++线程
  assert(THREAD->is_Java_thread(), "must be a Java thread");
  bool do_resolve = false;
  bool in_error = false;
 
  //用于创建目标类Class即InstanceMirrorKlass实例的Handle
  Handle mirror_handle;
 
  Symbol* name = NULL;
  Handle       loader;
  //获取常量池的锁
  {  MonitorLockerEx ml(this_oop->lock());
    //进一步校验,如果未解析
    if (this_oop->tag_at(which).is_unresolved_klass()) {
       //如果是因为解析异常导致未解析,则将in_error置为true
      if (this_oop->tag_at(which).is_unresolved_klass_in_error()) {
        in_error = true;
      } else {
         //准备开始解析
        do_resolve = true;
         //获取完整类名
        name   = this_oop->unresolved_klass_at(which);
        //根据当前常量池所属的Klass实例获取加载该Klass实例的类加载器
        loader = Handle(THREAD, this_oop->pool_holder()->class_loader());
      }
    }
  } // unlocking constantPool
 
 
  //解析失败抛出异常
  if (in_error) {
    Symbol* error = SystemDictionary::find_resolution_error(this_oop, which);
    guarantee(error != (Symbol*)NULL, "tag mismatch with resolution error table");
    ResourceMark rm;
    // exception text will be the class name
    const char* className = this_oop->unresolved_klass_at(which)->as_C_string();
    THROW_MSG_0(error, className);
  }
 
  if (do_resolve) {
    // 执行resolve_or_fail时常量池必须处于非锁定状态
    oop protection_domain = this_oop->pool_holder()->protection_domain();
    Handle h_prot (THREAD, protection_domain);
    //通过SystemDictionary调用类加载器Oop解析目标类
    Klass* k_oop = SystemDictionary::resolve_or_fail(name, loader, h_prot, true, THREAD);
    KlassHandle k;
    if (!HAS_PENDING_EXCEPTION) {
 
      //如果解析成功
      k = KlassHandle(THREAD, k_oop);
      //初始化mirror_handle
      mirror_handle = Handle(THREAD, k_oop->java_mirror());
      //校验访问权限
      verify_constant_pool_resolve(this_oop, k, THREAD);
    }
 
    //解析失败,必须记录具体的失败原因,这样后面的解析都会以同样的原因失败
    if (HAS_PENDING_EXCEPTION) {
      ResourceMark rm;
      Symbol* error = PENDING_EXCEPTION->klass()->name();
 
      bool throw_orig_error = false;
      {
        MonitorLockerEx ml(this_oop->lock());
 
        //校验是否因为并发导致解析失败,即有一个线程解析成功
        if (this_oop->tag_at(which).is_klass()) {
          CLEAR_PENDING_EXCEPTION;
          entry = this_oop->resolved_klass_at(which);
          //返回解析成功的结果
          return entry.get_klass();
        }
 
        if (!PENDING_EXCEPTION->
              is_a(SystemDictionary::LinkageError_klass())) {
          //如果不是链接类错误则直接抛出异常不用记录,如StackOverflow
        }
        else if (!this_oop->tag_at(which).is_unresolved_klass_in_error()) {
          //记录解析失败异常
          SystemDictionary::add_resolution_error(this_oop, which, error);
          this_oop->tag_at_put(which, JVM_CONSTANT_UnresolvedClassInError);
        } else {
          //如果是其他线程已经记录了异常原因则获取具体原因然后抛出异常
          error = SystemDictionary::find_resolution_error(this_oop, which);
          assert(error != NULL, "checking");
          throw_orig_error = true;
        }
      } // unlocked
      
      //抛出异常
      if (throw_orig_error) {
        CLEAR_PENDING_EXCEPTION;
        ResourceMark rm;
        const char* className = this_oop->unresolved_klass_at(which)->as_C_string();
        THROW_MSG_0(error, className);
      }
 
      return 0;
    }
    //如果需要根据类加载并且当前类不是数组类型的
    if (TraceClassResolution && !k()->oop_is_array()) {
      ResourceMark rm;
      int line_number = -1;
      const char * source_file = NULL;
      if (JavaThread::current()->has_last_Java_frame()) {
        // try to identify the method which called this function.
        vframeStream vfst(JavaThread::current());
        if (!vfst.at_end()) {
          //获取触发当前解析的代码行号和源代码文件名
          line_number = vfst.method()->line_number_from_bci(vfst.bci());
          Symbol* s = vfst.method()->method_holder()->source_file_name();
          if (s != NULL) {
            source_file = s->as_C_string();
          }
        }
      }
      //如果待加载类不是当前常量池所属的类
      if (k() != this_oop->pool_holder()) {
        if (source_file != NULL) {
          tty->print("RESOLVE %s %s %s:%d\n",
                     this_oop->pool_holder()->external_name(),
                     InstanceKlass::cast(k())->external_name(), source_file, line_number);
        } else {
          tty->print("RESOLVE %s %s\n",
                     this_oop->pool_holder()->external_name(),
                     InstanceKlass::cast(k())->external_name());
        }
      }
      return k();
    } else {
      MonitorLockerEx ml(this_oop->lock());
      //获取锁进一步校验常量池该项数据是否未解析
      do_resolve = this_oop->tag_at(which).is_unresolved_klass();
      if (do_resolve) {
        //如果未解析则将接解析结果存入常量池
        this_oop->klass_at_put(which, k());
      }
    }
  }
 
  entry = this_oop->resolved_klass_at(which);
  assert(entry.is_resolved() && entry.get_klass()->is_klass(), "must be resolved at this point");
  return entry.get_klass();
}

SystemDictionary

SystemDictionary类的定义在classfile/systemDictionary.hpp中,是一个系统字典类,用于保存所有已经加载完成的类,通过一个支持自动扩容的HashMap保存,key是表示类名Symbol指针和对应的类加载器oop指针,value是对应的Klass指针,当一个新的类加载完成后就会在SystemDictionary中添加一个新的键值对,其类继承关系如下:


image.png

SystemDictionary定义的属性和方法基本都是静态的,其关键属性如下:

  • _sdgeneration:int变量,保存已加载类的HashMap的容量
  • _dictionary:Dictionary类指针,实际保存已加载类的HashMap
  • _placeholders:PlaceholderTable类指针,当类加载的过程中临时存储键值对的地方,底层数据结构同Dictionary类
  • _shared_dictionary:Dictionary类指针,共享架构下用于保存已加载类的HashMap
  • _number_of_modifications: int变量,发生修改的次数,类加载或者删除都会增加该计数器
  • _system_loader_lock_obj:oop指针,系统类加载器的对象锁
  • _loader_constraints:LoaderConstraintTable类指针,保存类加载器加载约束的HashTable
  • _resolution_errors:ResolutionErrorTable类指针,保存类解析错误的HashTable
  • _invoke_method_table:SymbolPropertyTable类指针,保存MethodHandle调用的解析结果
  • _java_system_loader:oop指针,系统类加载器的引用

其定义 的方法主要有以下几类:

  • 属性操作的相关方法,如check_constraints,add_placeholder,add_klass,dictionary等
  • 根据类加载器和类名加载类的方法,如resolve_or_fail,resolve_or_null,resolve_super_or_fail
  • 根据class文件流,类加载器和类名加载类的方法,如parse_stream,resolve_from_stream
  • 根据类名和类加载器从已经加载的类中查找目标类,如find,find_instance_or_array_klass
  • 根据符号引用解析MethodHandle调用的方法,如find_method_handle_invoker,find_method_handle_type

综上可知SystemDictionary相当于类加载的一个统一入口,同时提供查找已加载的类和加载新的类的服务,实现逻辑比较复杂,有兴趣的可以参考源码。

ClassLoader

ClassLoader类的定义在classfile/classLoader.hpp中,ClassLoader就是传说中的用于加载Java核心类文件如rt.jar的启动类加载器的实现,其类继承关系如下:

image.png

ClassLoader定义的属性大都是用于统计类加载性能的计数器,如_perf_class_parse_time,PerfCounter类指针,用于统计类解析的累计耗时,除此之外有以下几个静态属性:

  • _first_entry:ClassPathEntry类指针,ClassPathEntry用于表示单个classpath路径,所有的ClassPathEntry实例以链表的形式关联起来,_first_entry表示链表的第一个实例
  • _last_entry:ClassPathEntry类指针,表示链表的最后一个实例
  • _num_entries:int变量,表示ClassPathEntry链表中ClassPathEntry实例的个数
  • _package_hash_table:PackageHashtable类指针,用于保存已经加载过的包名

ClassLoader定义的方法不多,大多是统计类加载性能相关的,除此之外有以下几个方法比较重要:

  • 加载zip文件读取写入等操作的动态链接库,load_zip_library方法
  • ClassPathEntry相关的,如setup_search_path,contains_entry,add_to_list,num_classpath_entries,classpath_entry,create_class_path_entry,update_class_path_entry_list等
  • 初始化的方法,initialize
  • 根据类名加载指定类文件的方法,load_classfile

其中initialize的调用链如下图:

image.png

其实现逻辑比较简单,关键代码如下:

void ClassLoader::initialize() {
  assert(_package_hash_table == NULL, "should have been initialized by now.");
  EXCEPTION_MARK;
 
  if (UsePerfData) {
    //如果开始性能检测则初始化各计数器
    NEWPERFTICKCOUNTER(_perf_accumulated_time, SUN_CLS, "time");
    NEWPERFTICKCOUNTER(_perf_class_init_time, SUN_CLS, "classInitTime");
  }
 
  //加载读写zip文件的动态链接库
  load_zip_library();
  //设置加载核心jar包的搜索路径,从系统参数Arguments中获取
  setup_bootstrap_search_path();
  //如果是惰性启动加载,即启动时不加载rt.jar等文件
  if (LazyBootClassLoader) {
    //设置meta_index_path,设置完成后会触发对meta_index_path下文件的解析
    setup_bootstrap_meta_index();
  }
}

load_classfile的调用链如下:


image.png

查看SystemDictionary的load_instance_class方法代码可知当类加载器oop为空的时候会调用load_classfile方法,即当其他的Java类加载器无法加载特定类的时候将类加载请求委托给启动类加载器加载,其关键代码如下:

instanceKlassHandle SystemDictionary::load_instance_class(Symbol* class_name, Handle class_loader, TRAPS) {
  instanceKlassHandle nh = instanceKlassHandle(); // null Handle
  //如果class_loader为空,即Java类加载器无法加载该类了
  if (class_loader.is_null()) {
    instanceKlassHandle k;
    //目标类未加载
    if (k.is_null()) {
      //执行目标类的加载
      k = ClassLoader::load_classfile(class_name, CHECK_(nh));
    }
 
    //如果已经加载则查找加载的类
    if (!k.is_null()) {
      k = find_or_define_instance_class(class_name, class_loader, k, CHECK_(nh));
    }
    return k;
  } else {
    ResourceMark rm(THREAD);
 
    assert(THREAD->is_Java_thread(), "must be a JavaThread");
    JavaThread* jt = (JavaThread*) THREAD;
    
    //构建JNI调用的参数,即类名
    Handle s = java_lang_String::create_from_symbol(class_name, CHECK_(nh));
    Handle string = java_lang_String::externalize_classname(s, CHECK_(nh));
    //调用的结果
    JavaValue result(T_OBJECT);
    //结果的处理器
    KlassHandle spec_klass (THREAD, SystemDictionary::ClassLoader_klass());
    //调用Java的类加载器加载特定类
    if (MustCallLoadClassInternal && has_loadClassInternal()) {
      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));
    }
    //从加载结果中获取目标类oop
    assert(result.get_type() == T_OBJECT, "just checking");
    oop obj = (oop) result.get_jobject();
 
    //检查访问权限
    if ((obj != NULL) && !(java_lang_Class::is_primitive(obj))) {
      instanceKlassHandle k =
                instanceKlassHandle(THREAD, java_lang_Class::as_Klass(obj));
      //检查类名是否一致
      if (class_name == k->name()) {
        return k;
      }
    }
    //类加载失败,返回空对象
    return nh;
  }
}

load_classfile方法的关键代码如下:

instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) {
  ResourceMark rm(THREAD);
  //获取类名
  const char* class_name = h_name->as_C_string();
  EventMark m("loading class %s", class_name);
  ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion);
 
  stringStream st;
  st.print_raw(h_name->as_utf8());
  st.print_raw(".class");
  //获取文件名
  const char* file_name = st.as_string();
  ClassLoaderExt::Context context(class_name, file_name, THREAD);
 
  //ClassFileStream表示Class文件的字节流
  ClassFileStream* stream = NULL;
  int classpath_index = 0;
  ClassPathEntry* e = NULL;
  instanceKlassHandle h;
  {
    //从第一个ClassPathEntry开始遍历所有的ClassPathEntry
    e = _first_entry;
    while (e != NULL) {
      stream = e->open_stream(file_name, CHECK_NULL);
      //如果检查返回false则返回null,check方法默认返回true
      if (!context.check(stream, classpath_index)) {
        return h; // NULL
      }
      //如果找到目标文件则跳出循环
      if (stream != NULL) {
        break;
      }
      e = e->next();
      ++classpath_index;
    }
  }
  //如果找到了目标class文件
  if (stream != NULL) {
    //构建一个ClassFileParser实例
    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,
                                                       context.should_verify(classpath_index),
                                                       THREAD);
    //如果解析异常
    if (HAS_PENDING_EXCEPTION) {
      ResourceMark rm;
      if (DumpSharedSpaces) {
        //打印异常
        tty->print_cr("Preload Error: Failed to load %s", class_name);
      }
      return h;
    }
    //调用ClassLoader的add_package方法,把当前类的包名加入到_package_hash_table中
    h = context.record_result(classpath_index, e, result, THREAD);
  } else {
    //没有找到目标文件
    if (DumpSharedSpaces) {
      tty->print_cr("Preload Warning: Cannot find %s", class_name);
    }
  }
 
  return h;
}
···
 再看另一个问题,启动类加载器是什么时候开始加载rt.jar等核心jar包的,是一次性把jar包中所有类都加载进去么?答案是需要的时候,当启动类执行main方法或者JVM自身启动的过程中需要用到rt.jar包中的某个类如java.lang.Thread等就会触发rt.jar的加载,加载特定需要的类。测试用例如下:
···
package jvmTest;
 
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
 
 
public class MainTest3 {
 
    public static void main(String[] args) {
 
        while (true) {
            try {
                System.out.println(getProcessID());
                Thread.sleep(600 * 1000);
            } catch (Exception e) {
 
            }
        }
    }
 
    public static final int getProcessID() {
        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
        System.out.println(runtimeMXBean.getName());
        return Integer.valueOf(runtimeMXBean.getName().split("@")[0])
                .intValue();
    }
 
}

执行main方法,启动HSDB,然后查找DriverManager类,该类是JDBC的驱动管理类,位于rt.jar中,结果如下:


image.png

ClassLoader JNI接口实现

ClassLoader JNI接口的实现源码在jdk/src/share/native/java/lang/ClassLoader.c中

findLoadedClass0方法

该方法实际调用JVM_FindLoadedClass方法,关键代码如下:

//JVM_ENTRY是宏定义,用于处理JNI调用的预处理,如获取当前线程的JavaThread指针
JVM_ENTRY(jclass, JVM_FindLoadedClass(JNIEnv *env, jobject loader, jstring name))
  JVMWrapper("JVM_FindLoadedClass");
  //THREAD表示当前线程
  ResourceMark rm(THREAD);
 
  Handle h_name (THREAD, JNIHandles::resolve_non_null(name));
  //获取类名对应的Handler
  Handle string = java_lang_String::internalize_classname(h_name, CHECK_NULL);
 
  //检查是否为空
  const char* str   = java_lang_String::as_utf8_string(string());
  if (str == NULL) return NULL;
 
  //判断类名是否超长
  const int str_len = (int)strlen(str);
  if (str_len > Symbol::max_length()) {
    return NULL;
  }
  //创建一个临时的Symbol
  TempNewSymbol klass_name = SymbolTable::new_symbol(str, str_len, CHECK_NULL);
 
  //获取类加载器对应的Handler
  Handle h_loader(THREAD, JNIHandles::resolve(loader));
  //查找目标类是否存在
  Klass* k = SystemDictionary::find_instance_or_array_klass(klass_name,
                                                              h_loader,
                                                              Handle(),
                                                          CHECK_NULL);
  //将Klass转换成Java中的Class                                                         
  return (k == NULL) ? NULL :
            (jclass) JNIHandles::make_local(env, k->java_mirror());
JVM_END

findBootstrapClass方法

该方法时委托启动类加载器加载特定类,该方法在检查类名合法后调用JVM_FindClassFromBootLoader完成加载,关键代码如下:

JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
                                              const char* name))
  JVMWrapper2("JVM_FindClassFromBootLoader %s", name);
 
  //检查类名是否合法
  if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
    return NULL;
  }
 
  //调用SystemDictionary解析目标类,如果未找到返回null
  TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
  Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
  if (k == NULL) {
    return NULL;
  }
  //将Klass转换成java中Class
  return (jclass) JNIHandles::make_local(env, k->java_mirror());
JVM_END

resolveClass0

该方法的本意是解析类,该方法实际并不会调用,只是兼容JDK1.1而保留了下来。该方法会调用JVM_ResolveClass完成解析,如下,OpenJDK只是提供了一个空实现。

image.png

defineClass0、defineClass1、defineClass2

defineClass0实际调用defineClass1的实现,defineClass1和defineClass2的区别就在于保存字节数据的数组是位于堆内存的普通数组还是位于元空间堆外内存的java.nio.ByteBuffer,两者的处理逻辑基本一致,就是将对应数组的数据拷贝到C++的字节数组中,然后调用JVM_DefineClassWithSource方法,最终调用jvm_define_class_common方法,该方法的关键代码如下:

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;
 
  // 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()) {
      THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), name);
    }
    //创建一个新的Symbol实例
    class_name = SymbolTable::new_symbol(name, str_len, CHECK_NULL);
  }
 
  ResourceMark rm(THREAD);
  //根据字节数组和文件名构建ClassFileStream实例
  ClassFileStream st((u1*) buf, len, (char *)source);
  //构建Java类加载器实例对应的Hanlder
  Handle class_loader (THREAD, JNIHandles::resolve(loader));
  //构建ProtectionDomain对应的Hander
  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);
  //将Klass实例转换成Java 的Class
  return (jclass) JNIHandles::make_local(env, k->java_mirror());
}

类链接

链接包含验证,准备和解析,其中符号引用的解析有ConstantPool完成,验证则是由Verifier类完成,该类的定义在classfile/verifier.hpp中,该类比较简单,核心方法就一个静态方法verify,其调用关系如下:

image.png

从调用关系可知,类链接的入口就是InstanceKlass::link_class_impl方法了,其关键代码如下:

bool InstanceKlass::link_class_impl(
    instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS) {
  //如果类状态异常
  if (this_oop->is_in_error_state()) {
    ResourceMark rm(THREAD);
    THROW_MSG_(vmSymbols::java_lang_NoClassDefFoundError(),
               this_oop->external_name(), false);
  }
  //如果类已链接则返回
  if (this_oop->is_linked()) {
    return true;
  }
 
 
  assert(THREAD->is_Java_thread(), "non-JavaThread in link_class_impl");
  JavaThread* jt = (JavaThread*)THREAD;
 
  instanceKlassHandle super(THREAD, this_oop->super());
  if (super.not_null()) {
    //如果父类是一个接口则抛出异常
    if (super->is_interface()) {  
      ResourceMark rm(THREAD);
      Exceptions::fthrow(
        THREAD_AND_LOCATION,
        vmSymbols::java_lang_IncompatibleClassChangeError(),
        "class %s has interface %s as super class",
        this_oop->external_name(),
        super->external_name()
      );
      return false;
    }
    //完成父类的链接
    link_class_impl(super, throw_verifyerror, CHECK_false);
  }
 
  //完成当前类实现的所有接口的链接
  Array* interfaces = this_oop->local_interfaces();
  int num_interfaces = interfaces->length();
  for (int index = 0; index < num_interfaces; index++) {
    HandleMark hm(THREAD);
    instanceKlassHandle ih(THREAD, interfaces->at(index));
    link_class_impl(ih, throw_verifyerror, CHECK_false);
  }
 
  //某些情况下链接父类的时候会把子类链接了,此时做检查是否已链接
  if (this_oop->is_linked()) {
    return true;
  }
 
  
  {
    //初始化对象锁
    oop init_lock = this_oop->init_lock();
    ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
    // rewritten will have been set if loader constraint error found
    // on an earlier link attempt
    // don't verify or rewrite if already rewritten
 
    if (!this_oop->is_linked()) {
      if (!this_oop->is_rewritten()) {
        {
          //完成字节码验证
          bool verify_ok = verify_code(this_oop, throw_verifyerror, THREAD);
          if (!verify_ok) {
            return false;
          }
        }
 
        //在校验是否验证完成
        if (this_oop->is_linked()) {
          return true;
        }
 
        // also sets rewritten
        this_oop->rewrite_class(CHECK_false);
      } else if (this_oop()->is_shared()) {
        ResourceMark rm(THREAD);
        char* message_buffer; // res-allocated by check_verification_dependencies
        Handle loader = this_oop()->class_loader();
        Handle pd     = this_oop()->protection_domain();
        //依赖约束检查
        bool verified = SystemDictionaryShared::check_verification_dependencies(this_oop(),
                        loader, pd, &message_buffer, THREAD);
        if (!verified) {
          THROW_MSG_(vmSymbols::java_lang_VerifyError(), message_buffer, false);
        }
      }
 
      //完成方法链接,即方法的入参和返回值的类型的链接
      this_oop->link_methods(CHECK_false);
      //初始化vtable和itable
      ClassLoaderData * loader_data = this_oop->class_loader_data();
      if (!(this_oop()->is_shared() &&
            loader_data->is_the_null_class_loader_data())) {
        ResourceMark rm(THREAD);
        this_oop->vtable()->initialize_vtable(true, CHECK_false);
        this_oop->itable()->initialize_itable(true, CHECK_false);
      }
      //设置类的状态为链接完成
      this_oop->set_init_state(linked);
      if (JvmtiExport::should_post_class_prepare()) {
        Thread *thread = THREAD;
        assert(thread->is_Java_thread(), "thread->is_Java_thread()");
        //发布JVMTI事件
        JvmtiExport::post_class_prepare((JavaThread *) thread, this_oop());
      }
    }
  }
  return true;
}

类初始化

查看link_class_impl方法的调用链可知类初始化的入口是InstanceKlass::initialize_impl方法,如下图:


image.png

该方法的关键代码如下:

void InstanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) {
  //完成此类的链接,如果已链接则会立即返回
  this_oop->link_class(CHECK);
 
  DTRACE_CLASSINIT_PROBE(required, InstanceKlass::cast(this_oop()), -1);
 
  bool wait = false;
 
  // Step 1
  {
    //获取对象锁
    oop init_lock = this_oop->init_lock();
    ObjectLocker ol(init_lock, THREAD, init_lock != NULL);
 
    Thread *self = THREAD; // it's passed the current thread
 
    // Step 2
    //如果正在初始化则等待初始化完成
    while(this_oop->is_being_initialized() && !this_oop->is_reentrant_initialization(self)) {
        wait = true;
      ol.waitUninterruptibly(CHECK);
    }
 
    //等待超时返回
    if (this_oop->is_being_initialized() && this_oop->is_reentrant_initialization(self)) {
      DTRACE_CLASSINIT_PROBE_WAIT(recursive, InstanceKlass::cast(this_oop()), -1,wait);
      return;
    }
 
    //初始化完成返回
    if (this_oop->is_initialized()) {
      DTRACE_CLASSINIT_PROBE_WAIT(concurrent, InstanceKlass::cast(this_oop()), -1,wait);
      return;
    }
 
    //状态异常,抛出异常
    if (this_oop->is_in_error_state()) {
      DTRACE_CLASSINIT_PROBE_WAIT(erroneous, InstanceKlass::cast(this_oop()), -1,wait);
      ResourceMark rm(THREAD);
      const char* desc = "Could not initialize class ";
      const char* className = this_oop->external_name();
      size_t msglen = strlen(desc) + strlen(className) + 1;
      char* message = NEW_RESOURCE_ARRAY(char, msglen);
      if (NULL == message) {
        // Out of memory: can't create detailed error message
        THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);
      } else {
        jio_snprintf(message, msglen, "%s%s", desc, className);
        THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message);
      }
    }
 
    // 设置状态,初始化进行中
    this_oop->set_init_state(being_initialized);
    this_oop->set_init_thread(self);
  }
 
  // Step 7
  //如果不是一个接口而是一个类则需要初始化它的父类
  if (!this_oop->is_interface()) {
     //获取父类
    Klass* super_klass = this_oop->super();
    //初始化父类
    if (super_klass != NULL && super_klass->should_be_initialized()) {
      super_klass->initialize(THREAD);
    }
    //实现的接口存在默认方法则初始化接口
    if (!HAS_PENDING_EXCEPTION && this_oop->has_default_methods()) {
      this_oop->initialize_super_interfaces(this_oop, THREAD);
    }
 
    //初始化异常,抛出异常
    if (HAS_PENDING_EXCEPTION) {
      Handle e(THREAD, PENDING_EXCEPTION);
      CLEAR_PENDING_EXCEPTION;
      {
        EXCEPTION_MARK;
        this_oop->set_initialization_state_and_notify(initialization_error, THREAD);
        CLEAR_PENDING_EXCEPTION;
      }
      DTRACE_CLASSINIT_PROBE_WAIT(super__failed, InstanceKlass::cast(this_oop()), -1,wait);
      THROW_OOP(e());
    }
  }
 
  // Step 8
  {
    assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl");
    JavaThread* jt = (JavaThread*)THREAD;
    DTRACE_CLASSINIT_PROBE_WAIT(clinit, InstanceKlass::cast(this_oop()), -1,wait);
    //执行静态方法
    this_oop->call_class_initializer(THREAD);
  }
 
  // Step 9
  if (!HAS_PENDING_EXCEPTION) {
    //设置状态初始化完成
    this_oop->set_initialization_state_and_notify(fully_initialized, CHECK);
    { ResourceMark rm(THREAD);
      debug_only(this_oop->vtable()->verify(tty, true);)
    }
  }
  else {
    //初始化失败,抛出异常
    Handle e(THREAD, PENDING_EXCEPTION);
    CLEAR_PENDING_EXCEPTION;
    JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
    {
      EXCEPTION_MARK;
      this_oop->set_initialization_state_and_notify(initialization_error, THREAD);
      CLEAR_PENDING_EXCEPTION;   // ignore any exception thrown, class initialization error is thrown below
      JvmtiExport::clear_detected_exception((JavaThread*)THREAD);
    }
    DTRACE_CLASSINIT_PROBE_WAIT(error, InstanceKlass::cast(this_oop()), -1,wait);
    if (e->is_a(SystemDictionary::Error_klass())) {
      THROW_OOP(e());
    } else {
      JavaCallArguments args(e);
      THROW_ARG(vmSymbols::java_lang_ExceptionInInitializerError(),
                vmSymbols::throwable_void_signature(),
                &args);
    }
  }
  DTRACE_CLASSINIT_PROBE_WAIT(end, InstanceKlass::cast(this_oop()), -1,wait);
}

你可能感兴趣的:(类加载、链接和初始化 C++源码解析)