类加载器
Java 虚拟机执行某个类,首先需要把类文件加载到虚拟机内存中,这个动作是通过类加载器来完成的。class 文件被类加载器加载到虚拟机内存中到卸载出内存经过了加载-验证-准备-解析-初始化-使用-卸载七个阶段。
从 JVM 角度来看有两种类加载器:启动类加载器和用户自定义类加载器。从开发人员角度来讲的话可分为四种:启动类加载器、扩展类加载器、应用程序类加载器、用户自定义类加载器。
启动类加载(Bootstrap ClassLoader)
使用 C 语言实现,是虚拟机的一部分,主要负责将存放在 ${JAVA_HOME}\lib
目录中的或者通过 -Xbootclasspath
参数指定路径中的并且可以被虚拟机识别(按照文件名识别) 的类库加载到虚拟机内存中。Java 程序不可以直接引用启动类加载器,开发者在自定义了自己的类加载器后,也要把类加载的请求委派给启动类加载器(双亲委派模型)。
扩展类加载器(Extension ClassLoader)
由 sun.misc.Launcher.ExtClassLoader
实现,主要负责加载${JAVA_HOME}\lib\ext
目录中或者被 java.ext.dir
系统变量指定路径的类库,开发人员可以直接使用扩展类加载器。
应用程序类加载器(Application ClassLoader)
由 sun.misc.Launcher.AppClassLoader
实现,负责加载用户类路径(Class Path)上指定的类库,开发者可以直接使用该加载器,一般情况下用户没有指定自己的类加载器,该加载器是程序的默认加载器。java.lang.ClassLoader#getSystemClassLoader
方法的返回值就是该类加载器,所以也叫做系统类加载器。
用户自定义类加载器
开发人员可以通过继承 ClassLoader
来实现自己的加载器。
类加载模型-双亲委派模型
双亲委派模型是类加载器之间的一种层次关系,双亲委派模型要求除了启动类加载器外,其他的类加载器都需要有自己的父类加载器。这种父子关系不是通过继承来实现,而是通过组合模式来复用父类加载器的代码。
当前类加载器收到了加载类的请求,先判断是否有父类加载器可以加载这个类,将加载请求委派给父类加载器(直到启动类加载),当父类无法加载时,子类加载器再尝试自己去加载,子类加载器无法加载时则抛出ClassNotFoundException
异常。双亲委派模型的优点:
- 防止一个类被重复加载(同一个 class 文件被不同的类加载器加载后是不同的类)
- 保护 Java 的核心类库,避免用户自定义类似于
java.lang.Object
、java.lang.Integer
类,对应用程序有一定安全保证
1 | /* |
破坏双亲委派模型
双亲委派模型是一种推荐的类加载器实现方式,从上面的代码中可以看到,自定义类加载器的实现的加载方法主要是在 findClass()
方法中实现的,在 loadClass
方法中如果父类加载器加载失败,就可以调用 findClass()
来加载,这样就可以保证实现双亲委派模型。
双亲委派模型是为了解决各个类加载器对于核心类统一加载的问题,如果基础类库中部分类需要调用用户代码,则双亲模型就要被破坏了,例如JNDI、JDBC、JCE、JAXB、JBI等。利用线程上下文类加载器(Thead Context ClassLoader)实现。
代码热替换、模块热部署等技术。