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

标签 java jvm classloader

我刚刚通读了 Java 虚拟机规范和 section on class loading让我很困惑。就我的一般理解和阅读规范后,我认为一个类的整体实例化由以下步骤组成,顺序如下:

  • 创建/加载 :类加载器定位表示类的字节流,可以是文件或网络流,也可以是任何实现类加载器以获取的字节流。如果找不到类,则 ClassNotFoundException被抛出。在这一点上,已经发生了一些基本的验证,其中 ClassFormatError如果字节数组不代表 Java 类(例如,缺少魔数(Magic Number))或 UnsupportedClassVersionError 则抛出如果正在运行的 JVM 实例不支持类版本。
  • 友情链接 :该类已挂接到 JVM。如果出现问题, LinkageError 的子类被抛出。链接包括三个子步骤:
  • 验证 : 确保字节流代表一个Java类,例如没有形式错误的字节码,例如方法字节码的操作数堆栈溢出。如果类验证失败,则 VerifyError被抛出。
  • 准备 :JVM 为所有静态字段分配内存,并可能创建实例模板以加快实例创建。创建虚拟方法表。在这个阶段不会抛出类加载特定的错误。 (尽管可能会抛出 OutOfMemoryError。)
  • 分辨率 :现在以运行时常量池的形式加载到方法区的所有符号引用都被解析为此 JVM 加载的实际类型。如果可以解析符号引用但导致定义冲突,则 IncompatibleClassChangeError被抛出。如果找不到引用的类,则 NoClassDefFoundError抛出基本上包裹了一个 ClassNotFoundException由试图加载此引用类的类加载器引发的。如果被引用的类引用自身,则 ClassCircularityError被抛出。解决方案可以以两种方式之一进行,这取决于 JVM 的实现者
  • 渴望 :对其他字段、方法或类的所有符号引用现在都已解决。
  • 懒人 :符号引用的解析被推迟到第一次使用方法。如果这个引用永远不需要被解析,那么这可能会带来一个引用不存在类的类永远不会抛出错误。
  • 初始化 : 类(class)的static运行在类中定义为 Java 代码的初始化程序。如果异常是由这样的初始化程序引起的,则该异常将被重新抛出,并包装在 ExceptionInInitializerError 中。 .

  • 令我困惑的是上述类加载机制的解析阶段。为什么解析被定义为链接中的一个明确步骤,特别是在准备之后发生?已经在description of the class loading phase内, 提到

    If C has any direct superinterfaces, the symbolic references from C to its direct superinterfaces are resolved using the algorithm of §5.4.3.1.



    由于验证是 described,所以在进行验证时是否也未解析符号引用:

    Verification (§4.10) ensures that the binary representation of a class or interface is structurally correct (§4.9). Verification may cause additional classes and interfaces to be loaded (§5.3) but need not cause them to be verified or prepared.



    我一直在脑海里想起这张照片

    Java class loading overview

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

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

    目前,我认为从该图像中取出分辨率阶段并将其声明为可以随时使用的通用过程是有意义的,因为在任何阶段可能需要有关其他类的信息,以便加载此类class 是必需的,当然也需要解析对此类的符号引用。从显示的图片来看,解决方案似乎只发生在一系列单独事件中的特定点。

    我怀疑这种对分辨率的描述是一个专门的步骤,可能只是那个时代的遗产,在这个时代,分辨率从来没有被懒惰地进行过,而是在所有剩余的符号引用都得到了解决的地方。

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

    最佳答案

    您的图片显示在准备后总是出现解析,但这不起作用。需要直接父类(super class)进行准备,因为您需要了解父类(super class)的实例字段以确定特定类的对象实例内存布局。此外,在使用类之前,即在创建实例或调用静态方法之前,必须执行类及其父类(super class)的静态初始化程序。

    这与所有其他引用类型的解析不同,后者可以延迟更长时间。允许在第一次调用方法之前解析方法中使用的类型。

    当你看第一章5.4.3. Resolution ,有明确说明:

    The Java Virtual Machine instructions anewarray, checkcast, getfield, getstatic, instanceof, invokedynamic, invokeinterface, invokespecial, invokestatic, invokevirtual, ldc, ldc_w, multianewarray, new, putfield, and putstatic make symbolic references to the run-time constant pool. Execution of any of these instructions requires resolution of its symbolic reference.



    所以区别就很明显了。有直接父类(super class)的解析和直接实现的接口(interface)(或在接口(interface)的情况下的超接口(interface))的解析,并且存在用于上述字节码指令的符号引用的解析可以推迟。

    关于java - Java 类加载的解析阶段实际上从哪里开始?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20347074/

    相关文章:

    java - JVM 的轻量级分布式单生产者多消费者消息服务

    java - 如何使用 jstat 或其他基于命令行的工具获取当前/最大 jvm 堆利用率(单个值)?

    java - 如何通过网络发送类

    Java 类加载器层次结构

    java:在运行时编译继承类

    java - 从 JAVA 源 AST 中删除所有方法调用

    java - 什么时候抛出java异常

    eclipse - Eclipse 的最佳 JVM 设置是什么?

    java - Joda-Time - 天数不增加

    java - javap -v 中的 "Signature"是什么