JVM架构
JVM有两种架构:
- 基于栈式架构
- 实现简单,移植性好,速度有限
- 基于寄存器架构
- 性能好,但是移植性差
JVM的生命周期:
- 启动
- 引导类加载器(bootstrap class loader)对JVM创建一个进程
- 运行
- 退出
- 有多种退出方式:异常退出、正常退出、手动退出、虚拟机异常退出
组成部分
- 类加载子系统
- 用于加载编译后的class文件到内存中,不检验class能否被执行
- 核心:类加载器、双亲委派模型、类加载过程
- 执行引擎子系统
- 将加载到内存的class字节码指令翻译为机器语言执行
- 运行时数据区
- PC程序计数器
- 本地方法栈
- 虚拟机栈(存放着栈帧)
- 元数据空间
- 堆空间
- 垃圾回收子系统
- GC主要是帮助Java管理内存
- 重点:垃圾回收期、垃圾回收算法、GC调优
- 本地接口和方法库
- 本地方法接口:用于融合不同的编程语言为java所用
类加载子系统
类加载阶段
- 加载
- 通过类的全限定名获取类的二进制字节流
- 将流转为元数据空间的运行时数据结构
- 生成Class对象代表这个类
- 链接
- 验证(确保Class正确性,防止危害虚拟机)
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
- 准备
- 只为类中的static变量分配内存,会指向数据类型的默认值并非编写过程中赋给的初值
- 解析
- 将类中对常量池的引用-->直接引用(静态链接)
- 常量池中的引用是符号引号(包含类的属性和方法,方法的符号引用包括方法的名称、返回值类型、参数类型)
- 直接引用指的是内存地址或者偏移量
- 解析过程并非一定在连接之后就执行,有时候会在初始化之后再执行
- 验证(确保Class正确性,防止危害虚拟机)
- 初始化
- 执行类构造器
<clinit>()过程(包含类中的静态变量的初始化和静态代码块中的逻辑) - 加载过程中先进行父类的
clinit,都只执行一次
- 执行类构造器
- 使用、卸载
解析阶段相当于静态链接,如果目标方法在编译器就知道,并且在运行期不变,就执行静态链接
类加载器
分为引导类加载器和自定义类加载器
所有继承ClassLoader的类加载器都是自定义的
常用的类加载器:
- Bootstrap ClassLoader:它是虚拟机自带的类加载器,用于加载Java平台核心库(rt.jar等),是最顶层的类加载器。
- 用C/C++实现,不继承于ClassLoader
- Extension ClassLoader:它用于加载Java平台扩展库(jre/lib/ext目录下的jar包),由Java类库扩展机制指定。
- System ClassLoader:它也称为Application ClassLoader,是用户自定义的类加载器,用于加载应用程序的类路径上指定的类库。
除了上述三种常用的类加载器,Java还提供了其他类型的类加载器,如:
- URLClassLoader:它可以从指定的URL中加载类。
- SecureClassLoader:它可以在加载类时提供安全性检查。
- ParallelClassLoader:它可以并行加载类,提高加载效率。
除此之外,每个类加载器都有自己的命名空间,用于存储被自身加载过的类的全限定名
双亲委派
问题提出
Java判定两个类是否相同是通过ClassLoaderId + PackageName + ClassName进行判定
如果有恶意攻击者伪装基本JDK中的类(利用全限定名),通过远程传输,就可能使得隐私代码被窃取
解决
类加载过程中,会把加载委托优先给父类加载器,依次到达顶层的加载器
加载过程(假设由Sys发出):
Sys->Ext->Boot(if not)->Ext(if not)->Sys(if not)->Error提示
优点
防止类被重复加载,以及核心API被篡改
双亲委派破坏者-线程上下文类加载器
SPI接口是用来提供第三方使用者的程序功能扩展的实现,在动态运行时启用:
- 它通常位于rt.jar中,由Bootstrap加载
- 但是第三方使用者实现接口的类不能被boot加载,并且双亲委派的过程是单向的
自定义类加载器
- 复杂的类加载器就通过继承ClassLoader类重写findClass()
- 如果不复杂就继承URLClassLoader类,就不需要自己写findClass()