JVM 类加载

1. 编译

源代码是成百上千个 “.java" 文件,并能被机器执行,需要编译为 ".class” 文件。常见的编译工具有 javac、maven、gradle 把 Java 工程或文件编译为 “*.java” 或 jar 包,war 包。

然后,编译后的 *.class 文件在合适的时机加载到 JVM 字节码执行引擎,最后由 JVM 字节码执行引擎解释执行。

在这里插入图片描述

2. JVM 类加载过程

一个类从加载到使用,一般会经历下面的过程:

加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载

2.1 加载

2.1.1 什么时候加载

在代码中使用到这个类的时候 .class 文件会被加载到 JVM 内存中。例如:

class TestClass {
	public static void main(String[] args) {
		User user = new User();
	}
}

main(String[] args) 方法是程序的入口,包含 main() 的 TestClass 类首先被加载,main 方法中的 User 类也会被加载到JVM内存中。

2.2 验证

验证阶段就是根据 Java 虚拟机规范,来校验加载进来的 .class 文件中的内容,是否符合指定的规范。

验证阶段是为了保证 Java 虚拟机的安全,防止 .class 文件被人篡改,导致运行错误。

2.3 准备

准备阶段有几种重要的工作:

  1. 给类分配内存空间(年轻代),如果JVM内存空间不够就会触发GC
  2. 给静态变量(static)分配内存空间,赋予默认初始值

2.4 解析

解析阶段,实际上是把符号引用替换为直接引用的过程,这部分内容很复杂,涉及到 JVM 的底层。

2.5 初始化(核心阶段)

初始化阶段包含几个重要的工作:

  1. 为成员变量分配内存空间并赋予默认值(只是开辟空间,并没有执行代码逻辑)
  2. 初始化静态代码快

2.5.1 什么时候会初始化一个类

一般来说有以下时机:new TestClass() 实例化类对象,如果类有父类则先初始化父类。

2.5 使用

使用阶段就是我们代码逻辑中的给类成员变量赋值,方法调用等。

2.6 卸载

卸载就是使用完成后,该对象没有任何引用时,GC 过程对象被回收掉。

3. 类加载器和双亲委派机制

类加载是需要类加载器来完成的。那么 Java 里有哪些类加载器呢?简单来说有下面四种类加载器。

3.1 启动类加载器(Bootstrap ClassLoader)

Bootstrap ClassLoader 类加载器主要负责加载我们在机器上安装的 Java 目录下的核心类的。也就是 Java 安装目录下 lib 包里边的 jar 包,这里包含 Java 最核心的一些类库,支持你的 Java 系统的运行。所以一旦你的 JVM 启动,那么首先就会依托 Bootstrap ClassLoader 加载器,去加载你的 Java 安装目录下的 lib 目录中的核心类库。

在这里插入图片描述

3.2 扩展类加载器(Extension ClassLoader)

Extension ClassLoader 加载器其实是类似的,加载 Java 安装目录下,有一个 lib/ext 目录中的核心类库。那么 JVM 一旦启动,就会加载 lib/ext 目录中的类。

3.3 应用程序类加载器(Application ClassLoader)

Application ClassLoader 加载器负责加载 ClassPath 环境变量所指定的路径中类,通俗的讲就是加载你写好的 Java 类加载到 JVM 内存中。

3.4 自定义类加载器

除了上面三种类加载器之外,还可以自定义类加载器,根据自己的需求加载类。

3.5 双亲委派机制

JVM 的类加载器是有亲子层级结构的,就是说启动类加载器是最上层的,扩展类加载器在第二层,第三城是应用程序类加载器,最后一层是自定义类加载器。

在这里插入图片描述

然后,基于这个亲子层级结构,就有一个双亲委派的机制。

假设应用程序类加载器要加载 TestClass 类,它首先会委派给自己的父类加载器去加载,最终传递到顶层类加载器加载,但是如果父类加载器在自己负责加载范围内,没有找到这个类,那么会下推加载权利给自己的子类加载器。

比如应用程序加载器加载 TestClass 的过程:

  1. 应用程序类加载器会委派给其父类加载器扩展类加载器加载类
  2. 扩展类加载器会委派给其父类加载器启动类加载器加载类,结果应用程序加载类没有找到该类,就会向下推给其子类加载器扩展类加载器去加载
  3. 扩展类加载器也没有找到该类,就会向下推给其子类加载器应用程序加载器去加载
  4. 最终应用程序加载器找到了 TestClass 类,然后加载到了 JVM 内存中

**双亲委派模型:**先找父类加载器去加载,不行的话再有子类加载器加载

3.6 思考

3.6.1 Java Web 系统一般采用 Tomcat 容器进行部署,那么 Tomcat 本身就是用 Java 写的,它自己就相当于一个 JVM,那么把 war 包放入 Tomcat 中是如何加载运行的呢?

在这里插入图片描述

Tomcat 自定义了 Common、Catalina、Shared 等类加载器,用来加载 Tomcat 自己的一些核心基础类库。

Tomcat 为每个部署在里边的 Java Web 应用程序都有一个对应的 WebApp 类加载器,负责加载部署的 Java Web 应用程序的类,至于 JSP 类加载器,则是给每个 JSP 都准备了一个 JSP 类加载器。

Tomcat 是打破了双亲委派机制的。

每个 WebAPP 负责加载自己对应的那个 Java Web 应用程序的 class 文件,也就是我们写好的某个系统打包好的 war 包中的所有 class 文件,不会传递给上层类加载器去加载。

图解Tomcat类加载机制(阿里面试题) - aspirant - 博客园 (cnblogs.com)

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值