Java链接器是如何工作的?

22

我想了解Java链接器是如何工作的,具体来说,它是按照什么顺序将类、接口、包、方法等组合成JVM可执行格式的。我在这里找到了一些信息(链接),但没有太多关于链接顺序的信息。


“Order”指什么?从该页面的第一句话开始:“Java虚拟机动态地加载、链接和初始化类和接口。”通过推断,大多数JVM都是按需执行此操作的。 - Vineet Reynolds
1
更重要的是,您所说的“链接器”是什么意思?Java中并没有这样的东西。 - user207421
5个回答

23

Java并没有所谓的“链接器”。但是,有一个类加载器的概念。它可以从“某处”获取Java字节码数组,创建一个类的内部表示,并将其与new等一起使用。

在这种情况下,接口只是特殊的类。当类被加载时,方法和字段就可以使用了。


9
OP提供了一个JVM规范的链接,明确提到在加载类后,JVM会进行链接作为其活动之一。 - Michael Borgwardt
1
当然,Java作为一个“链接器”。即使JLS没有明确将该过程命名为“链接”(请参见JLS 5.4 Linking),仍然存在一个“概念”(重用您喜欢的词)作为链接器。 - SyntaxT3rr0r
1
@SyntaxT3rr0r,不必嘲讽。随意写出更好的答案。 - Thorbjørn Ravn Andersen
1
@MichaelBorgwardt 问题中的措辞类似于C链接器,它(至少曾经)是一个完全独立的步骤,在磁盘上创建新文件。JVM不会将此作为单独的步骤,而是在内存中动态进行。 - Thorbjørn Ravn Andersen
@RyanTheLeach 不是真的。这个问题涉及到将一些东西组合成一个 JVM 可执行格式,这不是 jlink 的工作。 - Thorbjørn Ravn Andersen
显示剩余3条评论

15
首先,方法始终是类的一部分。接口基本上只是一种特殊的类,而包只是类的完全限定名称的一部分,对类文件的可见性和物理组织有一定影响。
因此,问题归结为:JVM如何链接类文件?你提供的JVM规范说:
Java编程语言允许在链接活动(由于递归关系,包括加载)发生的时间上具有实现灵活性,只要尊重语言的语义,即在初始化类或接口之前完全验证和准备它,并且在链接过程中检测到的错误在程序执行需要涉及错误所涉及的类或接口的点抛出。
例如,实现可以选择单独解析类或接口中的每个符号引用,仅在使用时进行(懒惰或迟解析),或者一次解析它们所有,例如,在验证类时(静态解析)。这意味着,在某些实现中,解析过程可能会在类或接口初始化后继续进行。
因此,该问题只能针对特定的JVM实现做出回答。
此外,这不应该对Java程序的行为产生任何影响,除非链接错误导致运行时抛出Error实例的确切点可能有所不同。

10

Java在链接方面与C不同。主要单元是类定义。类引用与其定义的匹配很多情况下发生在运行时。因此,您可以针对一个库的一个版本编译一个类,并在运行时提供另一个版本。如果相关的签名匹配,则一切都没问题。在编译时有一些常量内联,但也仅限于此。


5
正如先前提到的,Java编译器没有链接器。然而,JVM具有链接阶段,在类加载后执行。JVM规范最好地定义了它: “链接类或接口涉及验证和准备该类或接口、其直接超类、其直接超级接口以及其元素类型(如果它是数组类型),如果需要的话。在类或接口中解析符号引用是链接的可选部分。” 此规范允许实现在何时进行链接活动(由于递归,包括加载)方面具有灵活性,只要保持以下所有属性: - 在链接之前完全加载类或接口。 - 在初始化之前完全验证和准备类或接口。 - 在程序采取某些操作并可能直接或间接地需要与错误相关的类或接口的链接时,抛出在链接期间检测到的错误。

https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4


2

链接是ClassLoader执行的三项活动之一。它包括验证、准备和(可选)解析。

验证:它确保.class文件的正确性,即检查该文件是否由有效的编译器正确格式化和生成。如果验证失败,我们会得到运行时异常java.lang.VerifyError。

准备:JVM为类变量分配内存并将内存初始化为默认值。

解析:它是用直接引用替换类型中的符号引用的过程。这是通过搜索方法区来定位所引用的实体完成的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接