您的位置 首页 java

java类加载器的底层原理是怎样的?

java类加载器 是用来加载类文件的类,负责加载本地文件系统、网络或其它来源的 java 字节码文件。 jvm 启动时会创建sun.misc.Launcher实例,在Launcher 构造方法 内部创建扩展类加载器(ExtClassLoader)和应用类加载器(AppClassLoader),默认使用Launcher的getClassLoader()方法返回的应用类加载器(AppClassLoader)的实例加载类文件。

‬类加载器分类

java中有四种类加载器:

  1. 引导类加载器BootstrapClassLoader,负责加载位于 jre 的lib目录下的核心类库,如rt.jar。
  2. 扩展类加载器ExtClassLoader,负责加载位于jre的lib目录下除核心类库外的ext目录中的类库或者被java.ext.dirs 系统变量 所指定的路径中的所有类库。
  3. 应用类加载器AppClassLoader,负责加载 Class path路径下的类。
  4. 自定义类加载器,负责加载用户自定义路径下的类。

‬双亲委派机制

类加载器是通过双亲委派机制来加载类的。每个类加载器实例都有一个父类加载器的引用(是组合而非继承关系),虚拟机内置的引导类加载器本身并没有父类加载器,但可以作为其它类加载器的父类加载器。当一个类加载器实例需要加载某个类时,会先把这个任务委派给它的父类加载器,然后依次向上委派。当到达顶层后,开始向下查找。首先由引导类加载器查找并加载类,若未加载成功,则把任务转交给扩展类加载器;若扩展类加载器未加载成功,则把任务转交给应用类加载器;若应用类加载器未加载成功,则返回给委派的发起者,由它去加载类。如果它们都没有加载成功,则抛出ClassNotFoundException,反之将找到的类加载到内存并返回这个类在内存中的Class实例对象。

‬使用双亲委派优点

  • 避免重复加载

父类加载器已经加载了该类,则子类加载器不会进行二次加载,保证一个java类在jvm中是唯一的。

  • 沙箱安全机制

防止 核心 类库被随意纂改,如自己写的 java.lang .String类全限定名和 jdk 核心类库里一样则不会加载成功。

‬判断两个类相同的依据

jvm在判断两个class是否相同时,不仅要判断两个类名是否相同,还要判断是否由同一个类加载器实例加载。只有两者同时满足的前提下jvm才认定这两个class是相同的。如一份class字节码被不同的类加载器实例加载时jvm会认为是两个不同的class。

类加载‬器源码解析

ClassLoader是一个类加载器抽象类,通过loadClass方法来加载java类,同时有一个父类加载器ClassLoader的引用parent。所有类加载器(包括自定义类加载器)都继承自ClassLoader抽象类。在loadClass方法中,首先调用findloadedClass方法查找已经被加载的类,若找到则返回该类对象,反之则判断父类加载器parent是否为空。若parent不为空则调用其loadClass方法委托父类加载器去加载,反之则调用findBootstrapClassOrNull方法查找引导类加载器是否加载过该类。若以上父类加载器能找到则返回该类对象,反之则调用类类加载器自身的findClass方法去加载该类。在findClass方法中根据传入类名到指定路径下去寻找类文件并读取到内存,然后defineClass方法将字节数组转成Class对象后返回。其中defineClass是native方法。一般自定义类加载器只需要重写findClass方法即可,若需要打破双亲委派机制就得重写loadClass方法。

‬打破双亲委派机制

  • 线程上下文类加载器

对于SPI(java提供的一套用来被第三方实现或者扩展的API)来说,有些接口是java核心库提供的,但实现却是来自不同厂商提供的Jar包。而java核心库是由引导类加载器进行加载的,引导类加载器不会加载这些Jar包,这就导致双亲委派机制无法满足SPI的需求。通过当前 线程 设置上下文类加载器,就可以由设置的上下文类加载器来实现对接口实现类的加载。类似解决方案在 jdbc 中也有体现。

  • 自定义类加载器

Tomcat 是个 web容器 ,需要保证每个应用程序的 类库 都是独立且相互隔离的,部署在同一个web容器中相同类库的相同版本可以共享,基于安全考虑web容器依赖的类库与应用程序的类库需要隔离,需要支持 jsp 文件被修改后编译成 class文件 加载到虚拟机中运行时不需要重启web容器。若使用双亲委派机制则无法加载两个相同类库的不同版本,因为全限定类名只有一份,无法做到相互隔离。另外jsp被修改后类名还是一样,故直接从方法区取已经存在的而不会重新加载类。Tomcat通过自定义类加载器WebappClassLoader向上委派时绕开应用类加载器,使用扩展类加载器来保证核心类库不会被从替换,然后先对自己的类进行加载来保证相互隔离。另外通过自定义类加载器JasperLoader来实现jsp文件被修改时热加载类,即web容器检测到jsp被修改时会建一个新的实例来替换掉旧的JasperLoader实例,以此来实现jsp热替换。


大家好,我是余生吃蔬菜。欢迎点赞评论转发,收藏加关注,一起学习共同成长。

文章来源:智云一二三科技

文章标题:java类加载器的底层原理是怎样的?

文章地址:https://www.zhihuclub.com/192303.shtml

关于作者: 智云科技

热门文章

网站地图