面试之类加载器

 https://blog.csdn.net/javazejian/article/details/73413292

  1. 加载,通.class文件创建Class对象
  2. 验证,包括安全验证
  3. 准备,为类变量(static修饰)赋初始值,默认值比如int0finnal修饰的在编译阶段完成了。
  4. 解析,析:主要将常量池中的符号引用替换为直接引用的过程有类或接口的解析,字段解析,类方法解析,接口方法解析
  5. 初始化类加载最后阶段,若该类具有超类,则对其进行初始化,成员变量真正赋值阶段。

 

加载器

  1. 启动(Bootstrap)类加载器

主要加载主要加载的是JVM自身需要的类出于安全考虑,Bootstrap启动类加载器只加载包名为javajavaxsun等开头的类它负责将 <JAVA_HOME>/lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中

   2.扩展(Extension)类加载器

它负责加载<JAVA_HOME>/lib/ext

         3.系统(System)类加载器

它负责加载系统类路径java -classpath-D java.class.path 指定路径下的类库

Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的Java虚拟机对class文件采用的是按需加载的方式Java虚拟机采用的是双亲委派模式即把请求交由父类处理

 

启动类加载器,由C++实现,没有父类。

拓展类加载器(ExtClassLoader),由Java语言实现,父类加载器为null

系统类加载器(AppClassLoader),由Java语言实现,父类加载器为ExtClassLoader

自定义类加载器,父类加载器肯定为AppClassLoader。

   要求除了顶层加载器之外,必须有自己的父类加载器。

  类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,如果没有,才由子加载器来加载。

 

loadClass通过双亲委派方式最后倘若仍没有找到,则使用findClass()方法去加载

 this.getClass().getClassLoder.loadClass("className")方法获取到class对象。

一般情况下,在自定义类加载器时,会直接覆盖ClassLoader的findClass()方法并编写加载规则,取得要加载类的字节码后转换成流,然后调用defineClass()方法生成类的Class对

defineClass()方法是用来将byte字节流解析成JVM能够识别的Class对象,通过网络传输字节,加载class

如果直接调用defineClass()方法生成类的Class对象,这个类的Class对象并没有解析(也可以理解为链接阶段,毕竟解析是链接的最后一步),其解析操作需要等待初始化阶段进行

esolveClass(Class≺?≻ c)
使用该方法可以使用类的Class对象创建完成也同时被解析

 

4.其他类结构图

即拓展类加载器ExtClassLoader和系统类加载器AppClassLoader,这两个类都继承自URLClassLoader,是sun.misc.Launcher的静态内部类。sun.misc.Launcher主要被系统用于启动主应用程序,ExtClassLoader和AppClassLoader都是由sun.misc.Launcher创建的,其类主要类结构如下

 

 

 

Launcher 构造器
public Launcher() {
// 首先创建拓展类加载器
ClassLoader extcl;
try {
extcl = ExtClassLoader.getExtClassLoader();
} catch (IOException e) {
throw new InternalError(
"Could not create extension class loader");
}

// Now create the class loader to use to launch the application
try {
//再创建AppClassLoader并把extcl作为父加载器传递给AppClassLoader
loader = AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
throw new InternalError(
"Could not create application class loader");
}

//设置线程上下文类加载器,稍后分析
Thread.currentThread().setContextClassLoader(loader);
//省略其他没必要的代码......
}
}

 


Lancher初始化时首先会创建ExtClassLoader类加载器,然后再创建AppClassLoader并把ExtClassLoader传递给它作为父类加载器

 

5.那么编写自定义类加载器的意义何在呢?

---当class文件不在ClassPath路径下,默认系统类加载器无法找到该class文件,在这种情况下我们需要实现一个自定义的ClassLoader来加载特定路径下的class文件生成class对象。

---当一个class文件是通过网络传输并且可能会进行相应的加密操作时,需要先对class文件进行相应的解密后再加载到JVM内存中,这种情况下也需要编写自定义的ClassLoader并实现相应的逻辑。

---当需要实现热部署功能时(一个class文件通过不同的类加载器产生不同class对象从而实现热部署功能),需要实现自定义ClassLoader的逻辑。

 

谓的热部署就是利用同一个class文件不同的类加载器在内存创建出两个不同的class对象(关于这点的原因前面已分析过,即利用不同的类加载实例),由于JVM在加载类之前会检测请求的类是否已加载过(即在loadClass()方法中调用findLoadedClass()方法),如果被加载过,则直接从缓存获取,不会重新加载。注意同一个类加载器的实例和同一个class文件只能被加载器一次,多次加载将报错,因此我们实现的热部署必须让同一个class文件可以根据不同的类加载器重复加载,以实现所谓的热部署
————————————————
版权声明:本文为CSDN博主「zejian_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/javazejian/article/details/73413292

posted @ 2021-03-05 18:44  longtengdama  阅读(95)  评论(0编辑  收藏  举报