一、虚拟机的两种架构模型
(一)基于栈的虚拟机(JVM)
1、优势:通过操作数栈与底层硬件解耦,实现跨平台特性;
2、缺陷:所有操作经过操作数栈与内存交互,性能相对较慢;
3、优化:通过JIT即时编译器将热点代码编译成机器码直接执行,兼顾性能。
(二)基于寄存器的虚拟机(Android、Lua等)
1、优势:寄存器与CPU直接交互,性能快;
2、缺陷:依赖底层硬件,与平台耦合;
二、JVM组成架构
(一)类加载子系统
负责将符合格式要求的class字节码信息加载到内存;
(二)执行引擎子系统
负责将字节码指令翻译成机器语言交由硬件执行;(解释器 + 即时编译器)
(三)运行时数据区
PC程序计数器、本地方法栈、虚拟机栈、元数据区、对空间;
(四)垃圾回收子系统(GC)
垃圾回收器、垃圾回收算法、GC调优;
(五)本地接口和本地方法库
Java调用非Java代码的接口。
三、类加载子系统
(一)类加载机制及过程
加载 ==》连接(验证、准备、解析)==》初始化
1、加载
(1)通过完全限定名在相应路径下查找.class文件,获取字节流数据;
(2)将静态存储结构转换为运行时存储结构;
(3)在堆内存中创建其Class对象;
(4)由类加载器完成,按需加载,可分为显式加载和隐式加载。
2、连接
(1)验证阶段:用于确保被加载的Class的正确性;
(2)准备阶段:为类中声明的静态(static)成员变量分配内存空间,初始化为默认值(零值);
(3)解析阶段:把对常量池中的符号引用转换为直接引用。
3、初始化
(1)为静态成员变量赋初始值,执行静态代码块;(按源码顺序执行)
(2)本质上是执行类的构造器方法<clinit>()的过程;
4、使用和卸载
(JVM自带的类加载器加载的类,在JVM生命周期中始终不会被卸载)
(二)类加载器(ClassLoader)
1、Bootstrap 引导类加载器
(1)BootstrapClassLoader,C++实现,JVM自身的一部分;
(2)负责加载<JAVA_HOME>\lib路径下的核心类库,-Xbootclasspath参数指定路径下的jar包;
2、Extention 拓展类加载器
(1)ExtClassLoader,由Sun公司实现;
(2)负责加载<JAVA_HOME>\lib\ext路径下的核心类库,系统变量-Djava.ext.dir指定路径下的类库;
3、Application 系统类加载器
(1)AppClassLoader,可以通过ClassLoader.getSystemClassLoader()获取;
(2)负责加载系统类路径java -classpath或-D java.class.path指定路径下的类库;
4、用户自定义类加载器
通过继承ClassLoader类实现。
5、关系
(1)BootstrapClassLoader 负责加载 ExtClassLoader,并将其父加载器设置为 BootstrapClassLoader;
(2)BootstrapClassLoader 继续加载 AppClassLoader,并将其父加载器设置为 ExtClassLoader;
(3)AppClassLoader 加载用户自定义类加载器,并将其父加载器设置为 AppClassLoader;
(4)代码无法获取 BootstrapClassLoader,返回为null;
(三)双亲委派机制
1、起因:类加载机制的隔离性问题
(1)每个类加载器都有自己的命名空间,用来保存被自己加载过的所有类的全限定名;
(2)JVM判断两个类是否相同的基准:ClassLoaderId + PackageName + ClassName;
(3)这将导致Java程序运行过程中,允许存在两个包名和类名完全一致的Class;
2、核心思想
(1)自下而上检查类是否已被加载;
(2)自上而下尝试加载类;
3、双亲委派过程
伪代码(源码ClassLoader.loadClass(String name, boolean resolve)通过递归调用实现):
// AppClassLoader 开始尝试加载类
if (App命名空间中已加载该类) {
return Class对象;
}
// 向上委派
if (Ext命名空间中已加载该类) {
return Class对象;
}
// 向上委派
if (Bootstrap命名空间中已加载该类) {
return Class对象;
}
// BootstrapClassLoader 开始尝试加载
if (该类在Bootstrap加载范围内) {
Bootstrap加载该类;
return Class对象;
}
// 向下尝试加载
if (该类在Ext加载范围内) {
Ext加载该类;
return Class对象;
}
// 向下尝试加载
if (该类在App加载范围内) {
App加载该类;
return Class对象;
} else {
// 加载失败,抛出异常
throw new ClassNotFoundException();
}
4、优势:
(1)避免了一个类在不同层级的加载器中重复加载;
(2)有效防止Java核心API类在运行时被篡改,保障核心类的安全性;
(四)自定义类加载器
1、需求
(1)加载经过网络传输的class文件;
(2)加载需要进行安全校验,需要解密的class文件;
(3)实现热部署;
2、实现方式
(1)继承 ClassLoader 类,重写 findClass() 方法;
(2)直接继承 URLClassLoader 类;
(五)SPI机制与线程上下文类加载器
1、Java的SPI机制
(1)定义:SPI(Service Provider Interface),一种可插拔机制,一种动态的服务发现机制,例如JDBC、JBI、JNDI等;
(2)SPI接口由Java核心库来提供,一般位于rt.jar包中;
(3)第三方实现的具体代码库一般被放在classpath的路径下;
(4)第三方在编写实现类时,会在工程内创建一个META-INF/services/目录,并在该目录下创建一个与服务接口名称同名的文件,文件中保存了该接口的实现类全限定名;
(5)在程序启动的时候,就会根据约定去找到所有符合规范的实现类进行加载;
2、线程上下文类加载器加载SPI接口的实现类
(1)程序启动时 BootstrapClassLoader 初始化 Ext 和 App 类加载器,App 会被 Launcher 设置为默认的线程上下文类加载器;
(2)BootstrapClassLoader 加载rt.jar包时,无法加载第三方类,此时使用SPI机制调用 ServiceLoader.load() ,使用线程上下文类加载器(默认为App)去加载第三方类;
(3)通过线程上下文类加载器就轻易打破了双亲委派机制;