Java类加载的解析阶段实际上从哪里开始?

Java类加载的解析阶段实际上从哪里开始?,java,jvm,classloader,Java,Jvm,Classloader,我刚刚读完Java虚拟机规范,这让我感到困惑。在阅读了规范之后,就我的总体理解而言,我认为类的整体实例化包括以下步骤,顺序如下: 创建/加载:类加载器定位表示类的字节流,可以是文件或网络流,也可以是实现类加载器以获取的任何内容。如果找不到类,则抛出ClassNotFoundException。此时,已经发生了一些基本的验证,如果字节数组不表示Java类(例如,缺少幻数),则抛出ClassFormatError,如果运行的JVM实例不支持类版本,则抛出UnsupportedClassVersio

我刚刚读完Java虚拟机规范,这让我感到困惑。在阅读了规范之后,就我的总体理解而言,我认为类的整体实例化包括以下步骤,顺序如下:

  • 创建/加载:类加载器定位表示类的字节流,可以是文件或网络流,也可以是实现类加载器以获取的任何内容。如果找不到类,则抛出
    ClassNotFoundException
    。此时,已经发生了一些基本的验证,如果字节数组不表示Java类(例如,缺少幻数),则抛出
    ClassFormatError
    ,如果运行的JVM实例不支持类版本,则抛出
    UnsupportedClassVersionError

  • 链接:该类连接到JVM中。如果出现问题,将抛出
    LinkageError
    的子类。链接由三个子步骤组成:

    • 验证:确保字节流表示一个Java类,例如,字节码没有形式错误,例如方法字节码的操作数堆栈溢出。如果类验证失败,将抛出
      VerifyError

    • 准备:JVM为所有静态字段分配内存,并可能创建一个实例模板以加速实例创建。创建虚拟方法表。此阶段不会引发类加载特定错误。(不过可能会抛出
      OutOfMemoryError

    • 解析:现在以运行时常量池的形式加载到方法区域的所有符号引用都解析为此JVM加载的实际类型。如果可以解析符号引用但导致定义冲突,则会抛出
      不兼容ClassChangeError
      。如果找不到引用的类,将抛出一个
      NoClassDefFoundError
      ,它基本上包装了一个
      ClassNotFoundException
      ,该异常是由试图加载该引用类的类加载器抛出的。如果引用的类引用自身,则抛出
      classcircularyError
      。解决方案可以采用两种方式之一,这取决于JVM的实现者

    • Eager:对其他字段、方法或类的所有符号引用现在都已解析

    • Lazy:符号引用的解析将推迟到首次使用方法时进行。这可能会导致引用不存在的类的类在不需要解析该引用的情况下不会抛出错误

  • 初始化:运行类中定义为Java代码的类的
    静态
    初始化器。如果异常是由此类初始值设定项引起的,则此异常将重新包装在
    异常InInitializeRerror

让我困惑的是上述类加载机制的解析阶段。为什么分辨率被定义为链接中的一个明确步骤,具体发生在准备之后?已经在报告中提到

如果C有任何直接的超级接口,则从C到 它的直接超界面是用迭代算法求解的 §5.4.3.1

由于验证是:

验证(§4.10)确保类的二进制表示 或接口结构正确(§4.9)。验证可能导致 需要加载的附加类和接口(§5.3),但不需要 使其得到验证或准备

我一直在想这张照片

资料来源:

我几乎在任何地方都看到过解释类加载的例子。不应将解决视为所有阶段、创建/加载、验证、链接和初始化的一部分的总体责任(因为解决可以惰性地完成)

目前,,我认为,将分辨率阶段从该图像中移除并声明它是一个可在任何时候使用的通用过程是有意义的,因为在任何阶段都可能需要关于其他类的信息,因此需要加载这样一个类,这也必然需要对该类的符号引用进行分辨率班级。从所示图片来看,分辨率似乎只发生在一系列独立事件中的特定点上

我怀疑,这种对决议的描述是一个专门的步骤,可能只是从一个决议从未被懒散地执行的时代遗留下来的,而是在所有剩余的符号引用被解决的地方


我想知道的内容:今天JVM中的解决方案是否应该像我描述的那样理解?还是我错了,分辨率仍然可以被理解为固定时间线中的一个专用步骤,正如图片所示?

很难说,但我认为您在文档中只发现了一点差异或歧义。文档中的步骤定义得不太准确,因此实现可能有点具体,步骤实际上可能有点重叠,等等。实现中的主要关注点可能是速度,而不是绝对的逻辑清晰性


试着研究一下OpenJDK的源代码,你可能会发现一些有趣的东西。

你的图片显示,在准备工作之后,解析总是出现,但这不起作用。准备时需要直接超类,因为您需要了解超类的实例字段,以确定特定类的对象实例内存布局。此外,类的静态初始值设定项及其