我对其他答案不太感兴趣;它们对我来说太模糊太抽象了。我更喜欢讲故事。下面是我试图给出的一个更好的答案。
一个BASIC的例子
假设现在是1985年,你在Apple II上写了一个简短的BASIC程序:
] 10 PRINT "HELLO WORLD!"
] 20 GOTO 10
到目前为止,您的程序只是源代码。它没有运行,我们可以说它不涉及“运行时”。
但现在我运行它:
] RUN
它是如何运行的?它如何知道如何将字符串参数从PRINT发送到物理屏幕?我当然没有在代码中提供任何系统信息,而且PRINT本身也不知道关于系统的任何信息。
相反,RUN实际上是一个程序本身——它的代码告诉它如何解析我的代码,如何执行它,以及如何向计算机的操作系统发送任何相关的请求。RUN程序提供了“运行时”环境,作为操作系统和源代码之间的一层。操作系统本身作为这个“运行时”的一部分,但当我们谈论像RUN程序这样的“运行时”时,我们通常并不打算包括它。
编译和运行时的类型
编译二进制语言
在某些语言中,必须先编译源代码才能运行。有些语言将你的代码编译成机器语言——它可以由你的操作系统直接运行。这种编译后的代码通常被称为“二进制”(即使其他类型的文件也是二进制的:)。
在这种情况下,仍然涉及到一个最小的“运行时”——但是这个运行时是由操作系统本身提供的。编译步骤意味着在代码运行之前检测到许多可能导致程序崩溃的语句。
C is one such language; when you run a C program, it's totally able to send illegal requests to the operating system (like, "give me control of all of the memory on the computer, and erase it all"). If an illegal request is hit, usually the OS will just kill your program and not tell you why, and dump the contents of that program's memory at the time it was killed to a .dump file that's pretty hard to make sense of. But sometimes your code has a command that is a very bad idea, but the OS doesn't consider it illegal, like "erase a random bit of memory this program is using"; that can cause super weird problems that are hard to get to the bottom of.
字节码的语言
其他语言(如Java、Python)将代码编译成操作系统不能直接读取的语言,但特定的运行时程序可以读取编译后的代码。这种编译后的代码通常被称为“字节码”。
The more elaborate this runtime program is, the more extra stuff it can do on the side that your code did not include (even in the libraries you use) -- for instance, the Java runtime environment ("JRE") and Python runtime environment can keep track of memory assignments that are no longer needed, and tell the operating system it's safe to reuse that memory for something else, and it can catch situations where your code would try to send an illegal request to the operating system, and instead exit with a readable error.
所有这些开销使它们比编译的二进制语言慢,但它使运行时强大而灵活;在某些情况下,它甚至可以在开始运行后引入其他代码,而不必重新开始。编译步骤意味着在代码运行之前检测到许多会导致程序崩溃的语句;强大的运行时可以防止你的代码做一些愚蠢的事情(例如,你不能“擦除这个程序正在使用的随机内存”)。
脚本语言
Still other languages don't precompile your code at all; the runtime does all of the work of reading your code line by line, interpreting it and executing it. This makes them even slower than "bytecode" languages, but also even more flexible; in some cases, you can even fiddle with your source code as it runs! Though it also means that you can have a totally illegal statement in your code, and it could sit there in your production code without drawing attention, until one day it is run and causes a crash.
这些通常被称为“脚本”语言;它们包括Javascript、Perl和PHP。其中一些提供了你可以选择编译代码以提高其速度的情况(例如,Javascript的WebAssembly项目)。所以Javascript可以让网站上的用户看到正在运行的确切代码,因为他们的浏览器提供了运行时。
这种灵活性还允许在运行时环境中进行创新,如node.js,它既是一个代码库,也是一个运行时环境,可以将Javascript代码作为服务器运行,这涉及到与试图在浏览器上运行相同代码的行为非常不同。