JVM类加载过程解析

类加载的基本过程

JVM类加载过程分为三个主要阶段:加载(Loading)链接(Linking)初始化(Initialization)。其中链接又可分为验证(Verification)、准备(Preparation)和解析(Resolution)三个阶段。

1. 加载阶段

  • 主要任务:查找并加载类的二进制数据

  • 具体操作

    • 通过类的全限定名获取类的二进制字节流

    • 将字节流所代表的静态存储结构转化为方法区的运行时数据结构

    • 在堆中生成一个代表该类的Class对象,作为方法区数据的访问入口

2. 链接阶段

2.1 验证(Verification)
  • 目的:确保被加载的类的正确性,保证JVM安全

  • 验证内容

    • 文件格式验证(魔数、版本号等)

    • 元数据验证(语义分析,如是否有父类、是否继承final类等)

    • 字节码验证(数据流和控制流分析)

    • 符号引用验证(解析阶段进行)

2.2 准备(Preparation)
  • 目的:为类的静态变量分配内存并设置初始值

  • 注意事项

    • 此时分配内存的仅包括类变量(static变量),不包括实例变量

    • 初始值通常是数据类型的零值(如0、0L、null、false等)

    • 如果静态变量是final常量,则直接赋值为代码中指定的值

2.3 解析(Resolution)
  • 目的:将符号引用转换为直接引用

  • 解析内容

    • 类或接口的解析

    • 字段解析

    • 类方法解析

    • 接口方法解析

3. 初始化阶段

  • 目的:执行类构造器<clinit>()方法的过程

  • 特点

    • <clinit>()方法由编译器自动收集类中所有类变量的赋值动作和静态代码块合并产生

    • JVM保证子类的<clinit>()执行前,父类的<clinit>()已经执行完毕

    • 接口中不能使用静态代码块,但可以有变量初始化赋值操作

类加载的时机

JVM规范严格规定了有且只有5种情况必须立即对类进行初始化(而加载、验证、准备自然需要在此之前完成):

  1. 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时

  2. 使用java.lang.reflect包的方法对类进行反射调用时

  3. 当初始化一个类时,发现其父类还未初始化,需先触发其父类的初始化

  4. JVM启动时,用户指定的主类(包含main()方法的类)

  5. 当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化

类加载器

类加载器的分类

  1. 启动类加载器(Bootstrap ClassLoader)

    • 由C++实现,是JVM的一部分

    • 负责加载<JAVA_HOME>/lib目录下的核心类库

  2. 扩展类加载器(Extension ClassLoader)

    • 由Java实现,继承自java.lang.ClassLoader

    • 负责加载<JAVA_HOME>/lib/ext目录下的类库

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

    • 也称为系统类加载器

    • 负责加载用户类路径(ClassPath)上的类库

双亲委派模型

  • 工作流程

    1. 当一个类加载器收到类加载请求时,首先不会自己尝试加载,而是委派给父类加载器

    2. 只有当父类加载器反馈无法完成加载请求时,子加载器才会尝试自己加载

  • 优点

    • 避免类的重复加载

    • 保证Java核心API不被篡改(沙箱安全机制)

破坏双亲委派模型的情况

  1. JDK1.2之前,还没有双亲委派模型

  2. JNDI、JDBC等服务使用线程上下文类加载器(Thread Context ClassLoader)

  3. 用户对程序动态性的追求(如代码热替换、模块热部署)

常见问题

  1. JVM类加载过程分为哪几个阶段?

    • 加载、链接(验证、准备、解析)、初始化

  2. 类加载器有哪些?双亲委派模型是什么?

    • 启动类加载器、扩展类加载器、应用程序类加载器

    • 双亲委派模型是类加载请求的委派机制,优先委派给父加载器

  3. 准备阶段和初始化阶段有什么区别?

    • 准备阶段为静态变量分配内存并设置零值

    • 初始化阶段执行静态代码块和静态变量赋值操作

  4. 什么情况下会触发类的初始化?

    • 如上所述的5种情况

  5. 如何打破双亲委派模型?

    • 继承ClassLoader并重写loadClass方法

    • 使用线程上下文类加载器

  6. 为什么需要双亲委派模型?

    • 保证Java核心类库的安全性

    • 避免类的重复加载

  7. <clinit>和<init>方法有什么区别?

    • <clinit>是类构造器方法,用于静态变量初始化和静态代码块

    • <init>是实例构造器方法,用于实例变量初始化和构造方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值