【问题标题】:How compilation and linking at runtime is happening?运行时的编译和链接是如何发生的?
【发布时间】:2015-07-31 07:35:29
【问题描述】:
在教程中,我遇到了一个新概念(对我而言),这是我从未想过的。实际上,我认为编译是一个完全运行前的过程。这是教程中的短语:“编译时间发生在链接时间(一个或多个编译文件的输出连接在一起时)和运行时(执行程序时)之前。在某些编程语言中,某些编译和链接发生在运行时”。
我的问题是:
- 预运行时编译和链接过程是否与运行时编译和链接完全不同?如果是,请说明主要区别。
- 如何标记在运行时需要编译(链接)的代码段以及这些信息保存在哪里? (这可能因语言而异,如果可能,请举一个具体的例子)。
非常感谢您的宝贵时间!
【问题讨论】:
标签:
compilation
linker
runtime
【解决方案1】:
运行时编译
我个人知道的最好的(最知名的)示例是 Java 使用的 just in time compilation。正如您可能知道的那样,Java 代码正在被编译成 Java 虚拟机可以解释的字节码。因此,它与 C++ 不同,后者首先完全(预处理)编译(并链接)成可执行文件,无需任何虚拟机即可由操作系统直接运行。
Java 字节码由虚拟机解释,虚拟机将它们映射到特定于处理器的指令。话虽如此,JVM 执行 JIT,它获取该字节码并将其(在运行时)编译成机器码。在这里,我们到达您的第二个问题。即使在 Java 中,它也可能取决于您使用的 JVM,但基本上有称为 hotspots 的代码片段,这些代码片段经常运行并且可能会被编译以提高应用程序的性能。这是在运行时完成的,因为普通编译器没有(或者很可能没有)所有必要的数据来正确判断哪些代码实际上经常运行。因此,JIT 需要某种运行时统计收集,它与程序执行并行完成,由 JVM 完成。收集什么样的统计数据,可以优化什么(在运行时编译)等取决于实现(由于内存和时间限制,您显然无法完成普通编译器所做的一切 - 猜想这部分回答了 第一个问题?您不会编译所有内容,并且通常在运行时编译中只支持一组有限的优化)。您可以尝试查找此类信息,但根据我的经验,它的文档通常非常糟糕且很难找到(至少在涉及官方资源时,而不是演示文稿/博客等)
运行时链接
Linker 是一双不同的鞋子。我们不能再使用 Java 示例了,因为它实际上并没有像 C 或 C++ 那样的 linker(相反,它有一个 classloader 负责加载文件并将其全部放入一起)。
通常链接是在编译步骤(静态链接)之后由链接器执行的,这有优点(无依赖关系)和缺点(更高的内存印记,因为我们不能使用共享库,当库编号发生变化时,您需要重新编译您的来源)。
运行时链接(动态/后期链接)实际上是由操作系统执行的,操作系统链接器的工作是首先加载共享库,然后将它们附加到正在运行的进程。此外,还有不同类型的动态链接:显式和隐式。这样做的好处是当版本号更改时不必重新编译源代码,因为它是动态的和库共享,但也有缺点,如果您有不同的程序使用相同的库但需要不同的版本(查找 DLL hell)。所以是的,这两个概念也完全不同。
这一切又是如何完成的,如何决定应该链接什么以及如何链接,是特定于操作系统的,例如 Microsoft 有 dynamic-link library 概念。