我对其他答案并不满意,它们对我来说太过模糊和抽象。我更喜欢通过故事进行思考。以下是我对更好答案的尝试。
一个BASIC的例子
假设现在是1985年,你在一台Apple II上编写了一个简短的BASIC程序:
] 10 PRINT "HELLO WORLD!"
] 20 GOTO 10
到目前为止, 你的程序只是源代码. 它还没有运行,我们可以说它没有涉及任何 "运行时".
但现在我要运行它:
] RUN
实际上是如何运行的?它如何知道将字符串参数从PRINT
发送到物理屏幕?我在代码中没有提供任何系统信息,而PRINT
本身也不知道我的系统。
相反,RUN
本身就是一个程序——它的代码告诉它如何解析我的代码,如何执行它,并如何向计算机操作系统发送任何相关请求。 RUN
程序提供了充当操作系统和源代码之间层的“运行时”环境。操作系统本身作为这个“运行时”的一部分,但通常我们在谈论像RUN
程序这样的“运行时”时并不打算包括它。
编译和运行时类型
已编译的二进制语言
在某些语言中,必须在运行之前编译源代码。一些语言将您的代码编译成机器语言,您的操作系统可以直接运行它。这个编译出来的代码通常称为“二进制文件”(尽管每种其他类型的文件也都是二进制的 :)
在这种情况下,仍然涉及到最小的“运行时”——但是该运行时由操作系统本身提供。编译步骤意味着许多会导致程序崩溃的语句在代码运行之前被检测到。
C就是这样一种语言;当您运行C程序时,它完全能够向操作系统发送非法请求(例如,“让我控制计算机上所有的内存,并抹掉它们”)。如果遇到非法请求,通常操作系统只会杀死您的程序,并且不告诉您原因,并将该程序在被杀死时的内存内容转储到一个相当难以理解的.dump
文件中。但有时候您的代码具有一个非常糟糕的命令,但是操作系统不认为它是非法的,比如“删除该程序正在使用的任意一位内存”的命令;那可能会导致超级奇怪的问题,很难搞清楚。
字节码语言
其他语言(如Java、Python)将您的代码编译成操作系统无法直接读取的语言,但特定的运行时程序可以读取您的编译代码。这个编译出来的代码通常称为“字节码”。
这个运行时程序越复杂,它就可以在您的代码中没有包含的地方(即使是您使用的库中)进行更多的额外工作——例如,Java运行时环境(“JRE”)和Python运行时环境可以跟踪不再需要的内存分配,并告诉操作系统它可以安全地将该内存重新用于其他用途,并且它可以捕获您的代码尝试向操作系统发送非法请求的情况,并以可读的错误退出。
所有这些开销使它们比已编译的二进制语言更慢,但使得运行时强大而灵活; 在某些情况下,它甚至可以在启动后拉入其他代码,而无需重新开始。编译步骤意味着许多会导致程序崩溃的语句在代码运行之前被检测到;强大的运行时可以防止您的代码做傻事(
运行时库
和一些控制代码以及一些状态(由操作系统提供)。 - Martin York