当前位置: 首页 > 知识库问答 >
问题:

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

顾宣
2023-03-14

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

>

链接:类挂接到JVM。如果出错,将抛出LinkageError的子类。链接包括三个分步骤:

>

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

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

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

    >

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

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

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

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

    如果C有任何直接超级接口,则使用§5.4.3.1的算法解析从C到其直接超级接口的符号引用。

    由于描述了验证,在进行验证时是否也未解析符号引用:

    验证(§4.10)确保类或接口的二进制表示在结构上是正确的(§4.9)。验证可导致加载额外的类和接口(§5.3),但无需导致验证或准备。

    我一直在想这张照片

    来源:http://www.programcreek.com

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

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

    我怀疑,这种将解决方案描绘成一个专门的步骤的说法可能只是一个时代的遗产,在那个时代,解决方案从来都不是懒惰地进行的,而是在所有剩余的象征性引用都得到解决的时候。

    我想知道的是:今天JVM中的解决方案应该按照我描述的方式来理解吗?或者我错了,分辨率仍然可以理解为固定时间线中的专用步骤,就像图像显示的那样?


  • 共有2个答案

    傅毅然
    2023-03-14

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

    尝试查看OpenJDK的源代码,您可能会发现一些有趣的东西。

    唐宇定
    2023-03-14

    你的照片显示了准备后总是出现的解决方案,但那是行不通的。直接超级类是需要准备的,因为您需要了解超级类的实例字段来确定特定类的对象实例内存布局。此外,类及其超级类的静态初始化器必须在使用类之前执行,即在创建实例或调用静态方法之前。

    这不同于所有其他引用类型的分辨率,这些类型可以推迟更长时间。允许在第一次调用方法之前解析方法中使用的类型。

    当你看第5.4.3章的开头。决议,有明确规定:

    虚拟机指令Javaanewarray、Checkcast、getfield、getSTA、instanceof、invokedynamic、调用接口、Invokes特别、Invokestatic、invokeviral、ldc、ldc_w、Multianewarray、new、putfield和putest对运行时常量池进行符号引用。执行任何这些指令都需要解析其符号引用。

    因此,两者之间的区别非常明显。对于直接超类和直接实现的接口(或者对于接口来说是超级接口)的解析很早就发生了,对于上述字节码指令,符号引用的解析可以推迟。

     类似资料:
    • 我无法理解以下几行之间的区别(http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.2 ) A) 准备工作包括将静态字段初始化为默认值。 b)类的初始化包括为类中声明的静态字段(类变量)执行初始化器。 这是否意味着'a'将在准备阶段分配默认值,'b'将在初始化阶段分配值:

    • 问题内容: 我认为,如果Java程序员这样做的时间足够长,就会遇到这种情况。您正在进行调试并更改类。当您重新运行该程序时,这些更改似乎没有被接受,但是旧类似乎仍在运行。您清理并重建所有内容,同样的问题。有时,这可能归结为类路径问题,同一类多次出现在类路径中,但是似乎没有一种简单的方法来确定要加载的类来自何处… 有什么方法可以找到已加载类的文件路径吗?如果从文件或文件中加载了类,则最好是可行的。有任

    • 问题内容: 我决定不安装Windows,现在将Debian作为默认操作系统运行。我一直在Windows中编写代码,尤其是在Visual Studio中编写代码。我目前正试图习惯于在Linux下编译我的代码。 尽管我仍然有很多文档需要阅读,并且不要期望你们对我来说太容易了,但仍然可以从入门那里获得一些指导。我有一些特定的问题,但随时可以提出/建议有关此主题的其他任何内容。 关于创建make文件的推荐

    • 本文向大家介绍Java类加载器和类加载机制实例分析,包括了Java类加载器和类加载机制实例分析的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Java类加载器和类加载机制。分享给大家供大家参考,具体如下: 一 点睛 1 类加载器负责将.class文件(可能在磁盘上,也可能在网络上)加载到内存中,并为之生成对应的java.lang.Class对象。 2 当JVM启动时,会形成由三个类加载器组

    • 本文向大家介绍Java语言中的自定义类加载器实例解析,包括了Java语言中的自定义类加载器实例解析的使用技巧和注意事项,需要的朋友参考一下 本文研究的主要是Java语言中的自定义类加载器实例解析的相关内容,具体如下。 自己写的类加载器 需要注意的是:如果想要对这个实例进行测试的话,首先需要在c盘建立一个c://myjava的目录。然后将相应的java文件放在这个目录中。并将产生的.clas文件放在

    • 我尝试在一个java swing应用程序的缓存实例附近设置两个Oracle Coherence。这里可以找到解决方案。我的案子有点复杂,这就是游戏开始的地方。 在我的情况下,有一个帐户服务。它可以有两个endpoint:SIT和UAT。为了创建两个这样的服务,我需要加载Coherence的两个“实例”,以便用系统变量(tangosol.Coherence.cacheConfig)覆盖endpoin