JVM(一)初识JVM及其类加载子系统

93 阅读4分钟

JVM架构

JVM有两种架构:

  • 基于栈式架构
    • 实现简单,移植性好,速度有限
  • 基于寄存器架构
    • 性能好,但是移植性差

JVM的生命周期:

  • 启动
    • 引导类加载器(bootstrap class loader)对JVM创建一个进程
  • 运行
  • 退出
    • 有多种退出方式:异常退出、正常退出、手动退出、虚拟机异常退出

组成部分

  • 类加载子系统
    • 用于加载编译后的class文件到内存中,不检验class能否被执行
    • 核心:类加载器、双亲委派模型、类加载过程
  • 执行引擎子系统
    • 将加载到内存的class字节码指令翻译为机器语言执行
  • 运行时数据区
    • PC程序计数器
    • 本地方法栈
    • 虚拟机栈(存放着栈帧)
    • 元数据空间
    • 堆空间
  • 垃圾回收子系统
    • GC主要是帮助Java管理内存
    • 重点:垃圾回收期、垃圾回收算法、GC调优
  • 本地接口和方法库
    • 本地方法接口:用于融合不同的编程语言为java所用

类加载子系统

类加载阶段

1680962268424.png

  • 加载
    • 通过类的全限定名获取类的二进制字节流
    • 将流转为元数据空间的运行时数据结构
    • 生成Class对象代表这个类
  • 链接
    • 验证(确保Class正确性,防止危害虚拟机)
      • 文件格式验证
      • 元数据验证
      • 字节码验证
      • 符号引用验证
    • 准备
      • 只为类中的static变量分配内存,会指向数据类型的默认值并非编写过程中赋给的初值
    • 解析
      • 将类中对常量池的引用-->直接引用(静态链接)
      • 常量池中的引用是符号引号(包含类的属性和方法,方法的符号引用包括方法的名称、返回值类型、参数类型)
      • 直接引用指的是内存地址或者偏移量
      • 解析过程并非一定在连接之后就执行,有时候会在初始化之后再执行
  • 初始化
    • 执行类构造器 <clinit>()过程(包含类中的静态变量的初始化和静态代码块中的逻辑
    • 加载过程中先进行父类的 clinit,都只执行一次
  • 使用、卸载

解析阶段相当于静态链接,如果目标方法在编译器就知道,并且在运行期不变,就执行静态链接

类加载器

分为引导类加载器和自定义类加载器

所有继承ClassLoader的类加载器都是自定义的

常用的类加载器:

  1. Bootstrap ClassLoader:它是虚拟机自带的类加载器,用于加载Java平台核心库(rt.jar等),是最顶层的类加载器。
    1. 用C/C++实现,不继承于ClassLoader
  2. Extension ClassLoader:它用于加载Java平台扩展库(jre/lib/ext目录下的jar包),由Java类库扩展机制指定。
  3. System ClassLoader:它也称为Application ClassLoader,是用户自定义的类加载器,用于加载应用程序的类路径上指定的类库。

除了上述三种常用的类加载器,Java还提供了其他类型的类加载器,如:

  1. URLClassLoader:它可以从指定的URL中加载类。
  2. SecureClassLoader:它可以在加载类时提供安全性检查。
  3. 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()