阿里云服务器ECS    
弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新 [咨询更多]
阿里云存储OSS
简单易用、多重冗余、数据备份高可靠、多层次安全防护安全性更强、低成本 [咨询更多]
阿里云数据库RDS
稳定可靠、可弹性伸缩、更拥有容灾、备份、恢复、监控、迁移等方面的全套解决方案 [咨询更多]
阿里云安全产品
DDoS高防IP、web应用防火墙、安骑士、sll证书、态势感知众多阿里云安全产品热销中 [咨询更多]
阿里云折扣优惠    
云服务器ECS、数据库、负载均衡等产品新购、续费、升级联系客服获取更多专属折扣 [咨询更多]
类加载器
2020-7-16    点击量:
  类加载器
  
  类加载器可以加载类,这些类被HotSpot加载后,都以Klass对象表示。涉及到的主要的类加载器有启动类加载器/引导类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)和应用类加载器/系统类加载器(Application ClassLoader)。
  
  1、引导类加载器/启动类加载器
  
  引导类加载器由ClassLoader类实现,这个ClassLoader类是用C++语言来实现的,它负责将 <JAVA_HOME>/lib目录、 -Xbootclasspath选项指定的目录或系统属性sun.boot.class.path指定的目录下的核心类库加载到内存中。
  
  用C++语言定义的类加载器及重要的函数如下:
  
  class ClassLoader::AllStatic {
  
  private:
  
  ...
  
  // 加载类
  
  static instanceKlassHandle load_classfile(Symbol* h_name,TRAPS);
  
  // 设置加载路径
  
  static void setup_bootstrap_search_path();
  
  public:
  
  // 初始化类加载器
  
  static void initialize();
  
  ...
  
  }
  
  load_classfile()方法可以根据类名加载类,具体实现如下:
  
  源代码位置:openjdk/hotspot/src/share/vm/classfile/classLoader.cpp
  
  instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) {
  
  // 获取类名
  
  const char* class_name = h_name->as_C_string();
  
  ....
  
  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);
  
  ...
  
  // 调用ClassLoader的add_package方法,把当前类的包名加入到_package_hash_table中
  
  h = context.record_result(classpath_index, e, result, THREAD);
  
  }
  
  ...
  
  return h;
  
  }
  

  parseClassFile()方法就是解析Class文件中的类、字段、常量池等信息,然后转换为C++内部的对等表示,如类元信息存储在InstanceKlass实例中,常量池信息存储在ConstantPool中,部分的C++对等实现(类模型)在之前已经介绍过,这里不再介绍。后续会详细介绍parseClassFile()方法解析Class文件的过程。
  
  2、扩展类加载器
  
  扩展类加载器由ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现,负责将 <JAVA_HOME >/lib/ext目录或者由系统变量-Djava.ext.dir所指定的目录中的类库加载到内存中。
  
  用Java语言编写的扩展类加载器的实现如下:
  
  源代码位置:
  
  static class ExtClassLoader extends URLClassLoader {
  
  /**
  
  * create an ExtClassLoader. The ExtClassLoader is created
  
  * within a context that limits which files it can read
  
  */
  
  public static ExtClassLoader getExtClassLoader() throws IOException
  
  {
  
  final File[] dirs = getExtDirs(); // 获取要加载类的加载路径
  
  ...
  
  return new ExtClassLoader(dirs); // 实例化扩展类加载器
  
  ...
  
  }
  
  /*
  
  * Creates a new ExtClassLoader for the specified directories.
  
  */
  
  public ExtClassLoader(File[] dirs) throws IOException {
  
  super(getExtURLs(dirs), null, factory); // parent传递的参数为null,所以并不是引导类加载器
  
  }
  
  private static File[] getExtDirs() {
  
  String s = System.getProperty("java.ext.dirs");
  
  File[] dirs;
  
  if (s != null) {
  
  StringTokenizer st = new StringTokenizer(s, File.pathSeparator);
  
  int count = st.countTokens();
  
  dirs = new File[count];
  
  for (int i = 0; i < count; i++) {
  
  dirs[i] = new File(st.nextToken());
  
  }
  
  } else {
  
  dirs = new File[0];
  
  }
  
  return dirs;
  
  }
  
  ...
  
  }
  

  ExtClassLoader类的构造函数中在调用父类的构造函数时,传递的第2个参数的值为null,这个值最终会赋值给parent字段,所以后面将会讲到,当这个字段的值为null时,ClassLoader类中实现的loadClass()方法会调用findBootstrapClassOrNull()方法加载类,最终会调用C++实现的ClassLoader类的相关方法。
  
  3、系统类加载器/应用类加载器
  
  系统类加载器由AppClassLoader(sun.misc.Launcher$AppClassLoader)实现,负责将由系统环境变量-classpath、-cp或系统属性java.class.path指定的路径下的类库加载到内存中。
  
  用Java语言编写的扩展类加载器的实现如下:
  
  源代码位置:

  
  static class AppClassLoader extends URLClassLoader {
  
  public static ClassLoader getAppClassLoader(final ClassLoader extcl)throws IOException {
  
  final String s = System.getProperty("java.class.path");
  
  final File[] path = (s == null) ? new File[0] : getClassPath(s);
  
  ...
  
  return new AppClassLoader(urls, extcl);
  
  }
  
  /*
  
  * Creates a new AppClassLoader
  
  */
  
  AppClassLoader(URL[] urls, ClassLoader parent) {
  
  super(urls, parent, factory); // parent参数是一个扩展类加载器实例
  
  }
  
  /**
  
  * Override loadClass so we can checkPackageAccess.
  
  */
  
  public Class loadClass(String name, boolean resolve)throws ClassNotFoundException{
  
  ...
  
  return (super.loadClass(name, resolve));
  
  }
  
  ...
  
  }

  
  在Launcher类的构造函数中实例化系统类加载器时,会调用getAppClassLoader()方法获取系统类加载器,传入的参数是一个扩展类加载器实例,这样系统类加载器的父加载器就变成了扩展类加载器。用户自定义的无参加载器的父类加载器默认就是AppClassloader加载器。
  
  4、构造类加载器实例
  
  HotSpot在启动过程中会在rt.jar包里面的sun.misc.Launcher类中完成扩展类加载器和系统类加载器的实例化,也会进行引导类加载器的初始化,也就是调用C++语言编写的ClassLoader类的initialize()方法。
  
  HotSpot在初始化时,会初始化一个重要的变量,定义如下:
  
  源代码位置:hotspot/src/share/vm/classfile/systemDictionary.cpp
  
  oop  SystemDictionary::_java_system_loader  =  NULL;
  
  这个属性保存系统类加载器实例,HotSpot在加载主类时会使用这个类加载器加载主类。属性在compute_java_system_loader()方法中初始化,调用链路如下:
  
  JavaMain()                                      java.c
  
  InitializeJVM()                                 java.c
  
  JNI_CreateJavaVM()                              jni.cpp
  
  Threads::create_vm()                            thread.cpp
  
  SystemDictionary::compute_java_system_loader()  systemDictionary.cpp

  
  方法的实现如下:
  
  void SystemDictionary::compute_java_system_loader(TRAPS) {
  
  KlassHandle system_klass(THREAD, WK_KLASS(ClassLoader_klass));
  
  JavaValue result(T_OBJECT);<br>  // 调用java.lang.ClassLoader类的getSystemClassLoader()方法
  
  JavaCalls::call_static(&result, // 调用Java静态方法的返回值存储在result中
  
  KlassHandle(THREAD, WK_KLASS(ClassLoader_klass)), // 调用的目标类为java.lang.ClassLoader
  
  vmSymbols::getSystemClassLoader_name(), // 调用目标类中的目标方法为getSystemClassLoader
  
  vmSymbols::void_classloader_signature(), // 调用目标方法的方法签名
  
  CHECK);
  
  // 获取调用getSystemClassLoader()方法的结果并保存到_java_system_loader属性中
  
  _java_system_loader = (oop)result.get_jobject();  // 初始化属性为系统类加载器/应用类加载器/AppClassLoader
  
  }

  
  通过JavaClass::call_static()方法调用java.lang.ClassLoader类的getSystemClassLoader()方法。JavaClass::call_static()方法非常重要,它是HotSpot调用Java静态方法的API,后面传经详细介绍。
  
  下面看一下getSystemClassLoader()方法的实现,如下:
  
  public static ClassLoader getSystemClassLoader() {
  
  initSystemClassLoader();
  
  if (scl == null) {
  
  return null;
  
  }
  
  return scl;
  
  }
  
  private static synchronized void initSystemClassLoader() {
  
  if (!sclSet) {
  
  sun.misc.Launcher l = sun.misc.Launcher.getLauncher(); // 获取Launcher实例
  
  if (l != null) {
  
  scl = l.getClassLoader();
  
  // ...
  
  }
  
  sclSet = true;
  
  }
  
  }
  
  调用Launcerh.getLauncher()方法获取Launcher实例,实例通过静态变量launcher来保存,静态变量的定义如下:
  
  1
  
  private static Launcher launcher = new Launcher();
  
  调用l.getClassLoader()方法获取类加载器实例,如下:
  
  public ClassLoader getClassLoader() {
  
  return loader; // 返回的loader就是Launcher类的loader,也就是系统类加载器AppClassLoader
  
  }
  
  Launcher()类的构造函数如下:
  
  public Launcher() {
  
  // Create the extension class loader
  
  ClassLoader extcl;
  
  try {
  
  // 首先创建了扩展类加载器
  
  extcl = ExtClassLoader.getExtClassLoader();
  
  } catch (IOException e) {
  
  throw new InternalError("Could not create extension class loader", e);
  
  }
  
  // Now create the class loader to use to launch the application
  
  try {
  
  // 以ExtClassloader作为父加载器创建了AppClassLoader
  
  loader = AppClassLoader.getAppClassLoader(extcl);
  
  } catch (IOException e) {
  
  throw new InternalError("Could not create application class loader", e);
  
  }
  
  // Also set the context class loader for the primordial thread.
  
  // 默认线程上下文加载器为AppClassloader
  
  Thread.currentThread().setContextClassLoader(loader);
  
  }

  
  可以看到有对ExtClassLoader与AppClassLoader实例创建的逻辑,这样HotSpot就可以通过_java_system_loader属性获取AppClassLoader实例,通过AppClassLoader实例中的parent属性使用ExtClassLoader。
联系客服免费领取更多阿里云产品新购、续费升级折扣,叠加官网活动折上折更优惠