java 溯本求源之基础(二十二)之--加载、链接和初始化详解

1. 引言

        Java虚拟机(JVM)在执行Java程序时,涉及三个重要过程:加载、链接和初始化。这些过程确保Java程序能够正确运行并符合语言规范。加载是将类或接口的二进制数据读入内存并转换为JVM内部的数据结构的过程;链接将这些数据整合到JVM的运行时状态中,包括验证、准备和解析;初始化是执行类构造器<clinit>方法的过程,用于初始化静态变量和执行静态代码块。通过深入了解这些过程,开发小伙伴们可以更好地理解和优化Java应用。

        另外开发小伙伴们可以在读该文章的时候理解成我们在 java  -jar 也好 java 直接启动也好,是我们再启动一个程序

2. 加载

        加载是JVM执行Java程序的第一步,它将类或接口的二进制数据读入内存,并将其转化为JVM内部的数据结构。

2.1 类加载的过程

类加载过程主要包括以下几个步骤:

  1. 通过类加载器读取类文件:类加载器(ClassLoader)负责从文件系统、网络或其他来源读取类文件。
  2. 将二进制数据转化为Class对象:将类文件的二进制数据转化为JVM可理解的Class对象。
  3. 分配内存:为新加载的类或接口分配内存,并初始化为默认值。
2.2 类加载器的类型

JVM中有三种主要的类加载器:

  1. 引导类加载器(Bootstrap ClassLoader):负责加载核心类库,如rt.jar
  2. 扩展类加载器(Extension ClassLoader):负责加载扩展类库,如ext目录中的类。
  3. 应用类加载器(Application ClassLoader):负责加载应用程序的类和库。

        当然这三种类加载器我们在介绍jar命令(java 溯本求源之基础(七)之 jar(上篇)-CSDN博客)的时候已经已经说过了,感兴趣的可以简单看一下

2.3 类加载器的工作机制

        双亲委派模型是类加载器的核心机制。具体工作流程如下:

  1. 类加载请求:当类加载器收到一个类加载请求时,它首先检查该类是否已经被加载。
  2. 委派给父类加载器:如果没有被加载,类加载器将请求委派给父类加载器。
  3. 父类加载器处理请求:父类加载器继续向上委派,直到引导类加载器。
  4. 加载类:如果父类加载器能够完成加载,则返回加载的类。如果不能,则由当前类加载器尝试加载类。

双亲委派模型保证了核心类库的统一性,避免了类的重复加载和冲突。

3. 链接

        链接是将类或接口的二进制数据整合到JVM运行时状态的过程,包括验证、准备和解析。

3.1 验证

        验证确保类文件的正确性和安全性,防止恶意代码执行。主要检查以下内容:

3.1.1 文件格式验证

        验证类文件的格式是否符合JVM规范。

3.1.2 字节码验证

        检查字节码指令的正确性和安全性,确保没有非法指令。

3.1.3 符号引用验证

        验证类、字段、方法的引用是否有效,确保符号引用能够正确解析。

3.2 准备

        准备阶段为类的静态变量分配内存,并初始化为默认值。这一步并不执行代码,只是设置内存布局。

3.3 解析

        解析是将常量池中的符号引用转化为直接引用。主要包括以下几个步骤:

3.3.1 类或接口解析

        将符号引用转化为类或接口的直接引用。

3.3.2 字段解析

        将符号引用转化为字段的直接引用。

3.3.3 方法解析

        将符号引用转化为方法的直接引用。

4. 初始化

        初始化是执行类构造器<clinit>方法的过程,用于初始化静态变量和执行静态代码块。

类构造器<clinit>方法

        类构造器<clinit>是JVM在类初始化时执行的特殊方法。它由编译器生成,用于初始化类的静态变量和静态代码块。每个类或接口最多有一个<clinit>方法,且无参数、无返回值。<clinit>方法在类加载和链接后,由JVM调用。

4.1 类初始化的条件

根据JVM规范,类或接口在以下几种情况下会被初始化:

  1. 执行特定的JVM指令

    • new:创建类实例的指令。例如,MyClass obj = new MyClass();。当JVM执行new指令时,如果类未初始化,则会先初始化类。
    • getstatic:读取静态字段的指令。例如,System.out。当JVM执行getstatic指令时,如果字段所在的类未初始化,则会先初始化类。
    • putstatic:写入静态字段的指令。例如,MyClass.staticField = value;。当JVM执行putstatic指令时,如果字段所在的类未初始化,则会先初始化类。
    • invokestatic:调用静态方法的指令。例如,Math.abs(value)。当JVM执行invokestatic指令时,如果方法所在的类未初始化,则会先初始化类。
  2. 方法句柄解析:首次调用通过方法句柄解析得到的java.lang.invoke.MethodHandle实例。这些方法句柄包括:

    • REF_getStatic:获取静态字段的方法句柄。
    • REF_putStatic:设置静态字段的方法句柄。
    • REF_invokeStatic:调用静态方法的方法句柄。
    • REF_newInvokeSpecial:调用构造方法的方法句柄。
  3. 反射调用:通过反射API对类进行反射调用。例如,使用Class.forName("MyClass")

  4. 子类初始化:当一个类的子类被初始化时,父类也会被初始化。

  5. 接口方法实现:实现接口的类被初始化时,如果接口中有非抽象方法。

  6. JVM启动:JVM启动时指定的主类,例如通过java -jar命令启动的主类。

4.2 初始化过程

初始化过程涉及多个步骤,以确保线程安全并处理可能的递归初始化请求:

  1. 获取初始化锁:每个类或接口都有一个唯一的初始化锁,用于同步初始化过程。
  2. 检查初始化状态:如果类正在被其他线程初始化,当前线程将等待直到初始化完成。
  3. 初始化静态字段:按顺序初始化类或接口的所有final static字段。
  4. 初始化超类和超接口:如果类的超类或超接口尚未初始化,递归地对其进行初始化。
  5. 执行初始化方法:执行类或接口的初始化方法(<clinit>)。
  6. 处理异常:如果初始化过程中抛出异常,设置类为错误状态并通知所有等待线程。
5. JVM启动过程概述

        JVM启动过程包括加载主类、执行main方法,并初始化所有依赖类。这个过程确保应用程序的正常启动和运行。

5.1 加载主类

        当执行java -jar命令时,JVM根据JAR文件中的清单(manifest)加载指定的主类。主类是包含main方法的类,作为程序的入口。

5.2 执行main方法

        主类的main方法被调用,这是Java应用程序的起点。main方法的签名是public static void main(String[] args),它接受一个字符串数组作为参数,用于传递命令行参数。

5.3 初始化依赖类

在主类执行过程中,所有被引用的类和接口会按照需要进行加载、链接和初始化。这个过程包括:

  • 加载类:通过类加载器读取类文件,并将其转化为JVM内部数据结构。
  • 链接类:包括验证、准备和解析,将类文件整合到JVM的运行时状态中。
  • 初始化类:执行类构造器<clinit>方法,初始化静态变量和执行静态代码块。

        JVM启动过程确保了Java应用程序的所有必要类和资源都已正确加载和初始化,从而保证程序的正常运行。

6. 类的卸载

        类的卸载是JVM在运行过程中将不再使用的类从内存中移除的过程。类的卸载有助于优化内存使用,避免内存泄漏。主要步骤包括:

  1. 确定不再使用的类:通过垃圾回收机制,确定哪些类不再被引用。
  2. 释放内存:将这些类占用的内存释放。
6.1 类卸载的条件

类的卸载需要满足以下条件:

  • 类的所有实例都已被垃圾回收
  • 该类的ClassLoader已被垃圾回收
  • 没有对该类的静态变量和方法的引用
6.2 类卸载的过程

类卸载过程由垃圾回收器(GC)负责,主要包括以下步骤:

  1. 标记阶段:GC标记所有不再被引用的类。
  2. 清除阶段:GC释放被标记的类及其相关资源。

类卸载的过程在优化内存使用方面起到了重要作用,特别是在长时间运行的应用程序中。

再强调下,后面会单独说垃圾回收!!!

7. 总结

        加载、链接和初始化是Java虚拟机在执行Java程序时的关键步骤。理解这些过程有助于开发小伙伴们更好地理解,调试和优化Java应用,提升代码的稳定性和性能。JVM的启动过程确保了应用程序的正常运行,而类的卸载则有助于优化内存使用,避免内存泄漏。

最后希望各位开发小伙伴点赞收藏加评论!!!!谢谢拉!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

L.S.V.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值