JVM笔记(一)-- JVM概述和类加载子系统

118 阅读5分钟

一、虚拟机的两种架构模型

(一)基于栈的虚拟机(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)通过线程上下文类加载器就轻易打破了双亲委派机制;