【JVM】类加载器-CSDN博客
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
【JVM】类加载器
文章目录
0. 类加载器概述
类加载器ClassLoader是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术。
1. 类加载器的分类
类加载器分为两类
- Java代码中实现的类加载器
- JVM底层源码实现的类加载器
jdk8和8之后版本的类加载器的设计差别较大jdk8及之前的版本中默认的类加载器有如下几种
- JVM底层实现(C++)
- 启动类加载器Bootstrap加载Java中最核心的类
- Java
- 扩展类加载器Extension允许扩展Java中比较通用的类
- 应用程序类加载器Application加载应用使用的类
1.1 启动类加载器
**启动类加载器Bootstrap ClassLoader**是由 Hotspot
虚拟机提供的使用C++编写的类加载器。默认加载Java安装目录/jre/lib下的类文件比如 rt.jartools.jarresources.jar等。
使用启动类加载器去加载用户jar包有两种方式
- 将jar包放入
jre/lib
目录下进行扩展- 不推荐尽可能不要去更改JDK安装目录中的内容因为就算将jar包放入该目录下也可能由于文件名不匹配的问题导致jar包不会正常的被加载。
- 使用参数进行扩展
- 推荐使用
-Xbootclasspath/a:路径/jar包名.jar
进行扩展。
- 推荐使用
1.2 Java中的默认类加载器
扩展类加载器和应用程序类加载器都说JDK中提供的、使用Java编写的类加载器。它们的源码都位于 sun.misc.Launcher
中是一个静态内部类。继承自 URLClassLoader
可以通过目录或者指定jar包将字节码文件加载到内存中。
1.2.1 扩展类加载器
**扩展类加载器Extension ClassLoader**是jdk中提供的、使用Java编写的类加载器。默认加载Java安装目录 /jre/lib/ext
下的类文件。
通过扩展类加载器去加载用户jar包的方式
- 放入
jre/lib/ext
下进行扩展。- 不推荐尽可能不要去更改jdk安装目录中的内容。
- 使用参数进行扩展
- 推荐使用
-Djava.ext.dirs=jar包目录
进行扩展这种方式会覆盖掉原始目录随意我们应该用;
windows或:
macos/linux追加上原始目录。
- 推荐使用
1.2.2 应用程序类加载器
应用程序类加载器AppClassLoader面向我们用户的加载器负责加载当前应用 classpath 下的所有 jar 包和类。
2. 双亲委派机制
由于JVM中有多个类加载器双亲委派机制的核心是解决一个类到底由谁加载的问题。
双亲委派机制的作用
- 保证类加载的安全性通过双亲委派机制避免恶意代码替换jdk中的核心类库比如
java.lang.String
确保核心类库的完整性和安全性。 - 避免重复加载双亲委派机制可以避免同一个类被多次加载。
双亲委派机制指的是当一个类加载器接收到加载类的任务时会自底向上查找是否加载过再由顶向下进行加载。
- 向上查找
- 向上查找如果已经加载过就直接返回Class对象加载过程结束。这样就能避免一个类重复加载。
- 向下加载
- 如果所有父类加载器都无法加载该类则由当前类加载器自己尝试加载。所以看上去是自顶向下尝试。
- 第二次再去加载相同的类仍会向上进行委派如果某个类加载器加载过就会直接返回。
每个Java实现的类加载器中都保存了一个成员变量名为 parent
的类加载器**可以理解为它的上级并不是继承关系。**应用程序类加载器的parent父类加载器是扩展类加载器而扩展类加载器的parent是空因为启动类加载器由C++实现无法在Java中获得。
2.1 类的双亲委派机制是什么
类的双亲委派机制是什么
- 当一个类加载器去加载某个类的时候会自底向上查找是否加载过如果加载过就直接返回Class对象如果一直到最顶层的类加载器都没有加载再自顶向下进行加载。
- 应用程序类加载器的父类加载器是扩展类加载器扩展类加载器的父类加载器是启动类加载器。
- 双亲委派机制的好处
- 避免恶意代码替换jdk中核心类库确保核心类库的完整性和安全性。
- 避免类被重复加载。
2.2 打破双亲委派机制
打破双亲委派机制的三种方式
- 自定义类加载器自定义类加载器并且重写
loadClass
方法就可以将双亲委派机制的代码去除。 - 线程上下文类加载器利用上下文类加载器加载类比如JDBC和JNDI等。
- Osgi框架的类加载器历史上Osgi框架实现了一套新的类加载器机制允许同级之间委托进行类的加载。
2.2.1 自定义类加载器
- 一个Tomcat程序中可以运行多个Web应用如果这两个应用中出现了相同限定名的类比如Servlet类Tomcat要保证这两个类都能加载并且它们应该是不同的类。
- 如果不打破双亲委派机制当应用类加载器加载Web应用1中的MyServlet之后Web应用2中相同限定名的MyServlet类就无法被加载了。
Tomcat使用了自定义类加载器来实现应用之间类的隔离。每一个应用会有一个独立的类加载器加在对应的类。
ClassLoader中包含了4个核心方法(双亲委派机制的核心代码就位于loadClass方法中)
打破双亲委派机制的关键就是重写 loadClass
方法中的逻辑。
2.2.2 线程上下文类加载器
JDBC中使用了 DriverManager
来管理项目中引入的不同数据库的驱动比如mysql驱动oracle驱动。
DriverManager
类位于 rt.jar 包中由启动类加载器加载。而依赖中的mysql驱动对应的类由应用程序类加载器来加载。这就违反了双亲委派机制。
DriverManager
使用SPI机制最终加载jar包中对应的驱动类。
那么SPI机制是如何获取到应用程序类加载器的呢
SPI中使用了线程上下文中保存的类加载器进行类的加载这个类加载器一般是应用程序类加载器。
public static <S> ServiceLoader<S> load(Class<S> service){
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service,cl);
}
完整流程
- 启动类加载器加载
DriverManager
- 在初始化
DriverManager
时通过SPI机制加载jar包中的mysql驱动 - SPI中利用了线程上下文类加载器(应用程序类加载器)去加载类并创建对象。
思考
JDBC案例真的打破了双亲委派机制吗
有两种说法
- 打破了双亲委派机制这种由启动类加载器加载的类委派应用程序类加载器去加载类的方式打破了双亲委派机制。
- 没有打破双亲委派机制类加载流程中没有违反双亲委派机制。因为
DriverManager
位于rt.jar包下由启动类加载器加载而mysql驱动位于classpath由应用程序类加载器加载没有问题。
2.3 OSGi模块化
历史上OSGi模块化框架。它存在同级之间的类加载器的委托加载。OSGi还使用类加载器实现了热部署(在服务不停止的情况下动态更新字节码文件到内存中)的功能。
3. 总结
- 类加载器的作用是什么
答类加载器(ClassLoader)负责在类加载过程当中获取字节码并加载到内存中转换成byte[]接下来调用虚拟机底层方法将byte[]转换成方法区和堆中的数据。
- 有几种常见的类加载器
答
- 启动类加载器加载核心类
- 扩展类加载器加载扩展类
- 应用程序类加载器加载应用classpath中的类
- 自定义类加载器重写findClass方法
- 什么是双亲委派机制
答每个Java实现的类加载器中都保存了一个成员变量叫 parent 的类加载器。自底向上查找是否加载过再由顶向下进行加载。避免核心类被应用程序重写并覆盖的问题提升了安全性。
- 怎么打破双亲委派机制
答
- 重写loadClass方法。
- JNDI、JDBC、JCE、JAXB和JBI等框架使用了SPI机制+线程上下文类加载器。
- OSGi实现了一套类加载机制允许同级类加载器之间互相调用。
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |