高级java每日一道面试题-2024年10月26日-JVM篇-JVM的类加载机制是什么?

如果有遗漏,评论区告诉我进行补充

面试官: JVM的类加载机制是什么?

我回答:

JVM(Java虚拟机)的类加载机制是指将Java类的字节码文件(即.class文件)所包含的数据读入内存,并生成数据的访问入口的一种特殊机制。这个机制确保了Java程序能够在运行时动态地加载、链接和初始化类。以下是JVM类加载机制的详细解释:

一、类加载的过程

JVM中类的加载过程分为五个主要阶段:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)和初始化(Initialization)。

1. 加载(Loading)
  • 概述 :加载阶段是类加载过程的第一个阶段,主要任务是将字节码文件从各种来源(如文件系统、网络、数据库等)加载到内存中,并将其转换为Class对象。
  • 过程
    1. 读取字节码:类加载器通过类的全限定名找到对应的.class文件。
    2. 生成二进制数据:将读取的字节码转换为二进制数据流。
    3. 创建Class对象:将.class文件中的二进制数据读取到内存中,并将其转换为方法区的运行时数据结构。在堆中生成一个代表该类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
2. 验证(Verification)
  • 概述:验证阶段确保加载的类字节码文件符合JVM规范,没有安全问题。
  • 过程
    1. 文件格式验证:验证字节码文件的格式是否正确,例如魔数、版本号等。
    2. 元数据验证:验证类的元数据信息,例如类的继承关系、方法和字段的描述符等。
    3. 字节码验证:验证字节码指令序列的有效性,确保不会破坏虚拟机的运行状态。
    4. 符号引用验证:验证类的符号引用是否可以被解析为直接引用。
3. 准备(Preparation)
  • 概述:准备阶段为类的静态变量分配内存,并设置默认初始值(零值)。
  • 过程
    1. 分配内存:为类的静态变量(类变量,被static修饰的变量)分配内存,并设置其初始值(注意,这里设置的初始值是Java数据类型的默认值,而不是代码中显式赋予的值)。
    2. 设置默认值 :将静态变量初始化为默认值,例如int类型的静态变量初始化为0,boolean类型的静态变量初始化为false等。
4. 解析(Resolution)
  • 概述:解析阶段将类的符号引用转换为直接引用。符号引用是以一组符号来描述所引用的目标,而直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。
  • 过程
    1. 类或接口的解析:将类或接口的符号引用解析为直接引用。
    2. 字段解析:将字段的符号引用解析为直接引用。
    3. 类方法解析:将类方法的符号引用解析为直接引用。
    4. 接口方法解析:将接口方法的符号引用解析为直接引用。
5. 初始化(Initialization)
  • 概述:初始化阶段是类加载过程的最后一个阶段,主要任务是执行类的初始化代码,即执行类的静态初始化块和静态变量的赋值操作。
  • 过程
    1. 执行静态初始化块:执行类中的静态初始化块。
    2. 执行静态变量赋值:执行类中的静态变量的赋值操作。
    3. 父类初始化:如果类有父类,先初始化父类。

二、类加载器

JVM预定义了三种类加载器:启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extensions ClassLoader)和应用程序类加载器(Application ClassLoader)。

  1. 启动类加载器

    • 负责加载$JAVA_HOME/lib目录中的jar包,或者被参数-Xbootclasspath指定的路径。
    • 由C++实现,不是ClassLoader子类。
  2. 扩展类加载器

    • 负责加载$JAVA_HOME/lib/ext目录或java.ext.dirs指定目录下的jar包。
    • 由Java代码实现,是ClassLoader子类,但父类加载器为null。
  3. 应用程序类加载器

    • 负责加载ClassPath路径的jar包,即应用程序jar包。
    • 由Java代码实现,父类加载器为ExtClassLoader。

此外,开发人员还可以自定义类加载器,通过继承ClassLoader类或者URLClassLoader,并重写其findClass(String name)方法来实现。

三、双亲委派机制

  • 概述:双亲委派模型是一种类加载器之间的层次关系模型,确保类的加载具有唯一性和层次性。
  • 过程
    1. 委派请求:当一个类加载器收到类加载请求时,它首先将请求委托给父类加载器。
    2. 递归委派:父类加载器再将请求委托给它的父类加载器,直到最顶层的启动类加载器(Bootstrap ClassLoader)。
    3. 加载类:如果父类加载器无法加载该类(即在它的搜索路径中没有找到该类),则子类加载器才会尝试自己加载该类。
    4. 加载成功 :如果类加载成功,返回Class对象。

四、类加载的时机

类加载的时机并不是在程序编译时确定的,而是在程序运行时根据需要动态加载的。以下是一些常见的类加载时机:

  1. 当使用new关键字创建对象时,会触发对应类的加载和初始化。
  2. 当访问或设置一个类的静态字段时,会触发该类的加载和初始化(被final修饰、编译器优化时已经放入常量池的静态字段除外)。
  3. 当调用一个类的静态方法时,会触发该类的加载和初始化。
  4. 当使用java.lang.reflect包的方法对类进行反射调用时,会触发该类的加载和初始化。
  5. 当初始化一个类时,如果其父类还未进行初始化,则会先触发其父类的初始化。

五、类的卸载

在类使用完成之后,如果满足以下条件,类就会被卸载:

  1. 该类所有的实例都已经被回收,即Java堆中不存在该类的任何实例。
  2. 加载该类的ClassLoader已经被回收。
  3. 该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。

如果以上三个条件全部满足,JVM就会在方法区垃圾回收的时候对类进行卸载。类的卸载过程实际上是在方法区中清空类信息,标志着Java类的整个生命周期的结束。

总结

综上所述,JVM的类加载机制是一个复杂而精细的过程,它确保了Java程序的动态性、安全性和稳定性。通过理解这个过程,我们可以更好地编写和维护Java应用程序。

相关推荐
星河梦瑾43 分钟前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
黄名富1 小时前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
love静思冥想1 小时前
JMeter 使用详解
java·jmeter
言、雲1 小时前
从tryLock()源码来出发,解析Redisson的重试机制和看门狗机制
java·开发语言·数据库
TT哇1 小时前
【数据结构练习题】链表与LinkedList
java·数据结构·链表
java1234_小锋1 小时前
JVM对象分配内存如何保证线程安全?
jvm
Yvemil71 小时前
《开启微服务之旅:Spring Boot 从入门到实践》(三)
java
Anna。。1 小时前
Java入门2-idea 第五章:IO流(java.io包中)
java·开发语言·intellij-idea
.生产的驴2 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
爱上语文2 小时前
宠物管理系统:Dao层
java·开发语言·宠物