乐趣区

浅谈java classloader

本文由作者张远道授权网易云社区发布。
类加载器三杰
jvm 有三类 classloader,分别是 bootstrap classloader,extended classloader 以及 system classloader。
bootstrap classloader 是系统在启动 jvm 时默认加载的。当用户在命令行输入 java Test 时,系统会首先加载 jvm。在 windows 系统下,jvm 的路径通常位于 %JAVA_HOME%/jdk/jre/client/jvm.dll 和 %JAVA_HOME%/jdk/jre/server/jvm.dll.
bootstrap classloader 加载后,会载入 extended classloader,并将 extended classloader 的父类设为 bootstrap classloader。然后,bootstrap classloader 接着载入 system classloader,并将 system classloader 的父类设为 extended classloader。至此,bootstrap–extended–system 三级继承结构形成。
bootstrap classloader 在 jvm 启动之后自动加载。bootstrap classloader 由 c 实现,不属于 java 类。
extended classloader 由 java 实现,通常为 sun.misc.Lancher$ExtClassLoader.
system classloader 由 java 实现,通常为 sun.misc.Lancher$AppClassLoader.
其中,
bootstrap classloader 负责加载 sun.boot.class.path 路径下的.class 文件以及 jar 包。
extended classloader 负责加载 java.ext.dirs 路径下的.class 文件以及 jar 包。
system classloader 负责加载 java.class.path 路径下的.class 文件以及 jar 包。
sun.boot.class.path 通常对应环境变量 CLASSPATH 的路径。
java.ext.dirs 通常对应 JAVA_HOME/jre/lib/ext 目录。
java.class.path 对应用户自身的类路径。
类加载到何处
据可靠情报,jvm 由方法区,堆,栈,pc 寄存器和本地方法栈构成。类加载器的任务就是将 class 二进制文件加载到方法区,供虚拟机模制出在堆中存放的对象。
双亲委托机制
classloader 加载类的过程为:
检查被加载类是否被加载。
如果没有被加载则调用父 classloader 加载该类。
如果 1、2 不成功,则仍由自身进行类加载。
这种机制又叫双亲委派机制。
双亲委派机制的好处是,避免多个类加载器加载同一个类的不同拷贝到内存(jvm 的方法区)中。因为如果类 A 由 ClassLoaderA 加载,同时,又被 ClassLoaderB 加载,这样,内存中就会存在两份不同的 A 的定义,于是形成 A 既是 ClassLoaderA 罩的,又是 ClassLoaderB 罩着,造成灾难性后果。
用户自定义类的加载顺序通常为:
首先调用 AppClassLoader 加载类,AppClassLoader 调用 ExtClassLoader,ExtClassLoader 调用 BootClassLoader,BootClassLoader 在 sun.boot.class.path 寻找改类,没找到,加载失败;ExtClassLoader 也未加载类,失败,最后由 AppClassLoader 加载成功。从这个加载顺序可以看出来,三个类加载器的对类的可见性是不同的。
java 中的类是由 java 的全名以及类的 classloader 来限定的。只有当二者完全一样才会认为是同一个类。否则是不同的类。因此,可以定义一个同名的类,包名也一样,只要保证该类被不同的类加载器加载即可。
当前类加载器和线程上下文类加载器
当前类加载器
当前类加载器是指当前方法所在的类使用的类加载器。在程序中使用 Class.forName 或者 Class.getResource 抑或 Class.class 时就是使用的该类加载器。
线程上下文类加载器
线程上下文类加载器可以不遵循双亲委派机制。线程的上下文类加载器有 Thread.currentThread().setContextClassLoader() 来为当前线程设置线程上下文类加载器。如果没有设置当前线程的上下文类加载器,则继承父类的上下文类加载器。
为什么还需要线程上下文类加载器?
考虑一种情况,当我们的程序必须由 jvm 的核心代码去加载第三方类的时候。比如 jndi,jndi 的核心是 rt.jar 包中实现的,由 Bootstrap classloader 负责加载,但是 jndi 必须加载第三方厂商的具体的 jndi 实现,这个时候调用 Bootstrap 加载只对其子类加载器可见的类,就会出现失败。这个时候就可以使用线程上下文类加载器。
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请访问网易云社区。
文章来源:网易云社区

退出移动版