运行时的编译和链接是如何发生的?

How compilation and linking at runtime is happening?

在教程中我遇到了一个新概念(对我来说),我从未想过它是可能的。实际上,我认为编译完全是运行时间之前的过程。这是教程中的短语:"Compile time occurs before link time (when the output of one or more compiled files are joined together) and runtime (when a program is executed). In some programming languages it may be necessary for some compilation and linking to occur at runtime".

我的问题是:

  1. 运行 前的编译和链接过程与 运行 时的编译和链接过程完全不同吗?如果是,请说明主要区别。
  2. 在 运行 时间内需要编译(链接)的代码段是如何标记的,这些信息保存在哪里? (这可能因语言而异,如果可能,请举出具体的例子)。

非常感谢您的宝贵时间!

运行时编译

我个人知道的最好(最著名)的例子是 Java 使用的 just in time compilation。如您所知,Java 代码被编译成字节码,可以由 Java 虚拟机解释。因此,它与 C++ 不同,C++ 首先完全(预处理)编译(并链接)成可执行文件,后者可以 运行 直接由 OS 执行,无需任何虚拟机。

Java 字节码由 VM 解释,将它们映射到处理器特定指令。话虽如此,JVM 执行 JIT,它获取字节码并将其编译(在 运行 时间内)为机器码。现在我们到达您的第二个问题。即使在 Java 中,它也可能取决于您使用的是哪个 JVM,但基本上有一些代码片段称为 hotspots,这些代码片段经常 运行 并且可能会被编译,从而提高应用程序的性能。这是在 运行 时间内完成的,因为普通编译器没有(或者很可能没有)所有必要的数据来正确判断哪些代码片段实际上 运行 频繁。因此,JIT 需要某种运行时间统计 收集,它与程序执行并行完成,由JVM 完成。收集什么样的统计数据,可以优化什么(在 运行 时间内编译)等取决于实现(由于内存和时间限制,你显然不能做普通编译器会做的所有事情 - 猜测这部分回答了第一个问题?您不会编译所有内容,通常在运行时间编译中只支持有限的一组优化。你可以尝试寻找这样的信息,但根据我的经验,通常它的记录非常糟糕并且很难找到(至少在涉及官方来源时,而不是 presentations/blogs 等)

运行时链接

Linker 是一双与众不同的鞋。我们不能再使用 Java 示例,因为它实际上没有像 C 或 C++ 那样的 linker(相反它有一个 classloader它负责加载文件并将它们放在一起)。

通常链接是在编译步骤(静态链接)之后由链接器执行的,这有优点(没有依赖性)和缺点(更高的内存印记,因为我们不能使用共享库,当库号改变时你需要重新编译你的源代码)。

运行时链接(dynamic/late 链接)实际上是由 OS 执行的,OS 链接器的工作是首先加载共享库然后将它们附加到 运行ning 进程。此外,还有不同类型的动态链接:显式和隐式。这样做的好处是当版本号发生变化时不必重新编译源代码,因为它是动态的和库共享,但也有缺点,如果你有不同的程序使用相同的库但需要不同的版本(寻找 DLL地狱)。所以是的,这两个概念也完全不同。

同样,这一切是如何完成的,它是如何决定应该链接什么以及如何链接的,都是 OS 具体的,例如 Microsoft 有 dynamic-link library 概念。