Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

语言运行时

复习

  • 动态链接与装载:运行时才把库接上
  • 程序在内存里的样子:代码区、数据区、栈和堆
  • 编译器:把程序翻译成可运行的形式

TL;DR

  • 语言运行时是高级语言背后的一组隐藏服务
  • 它负责内存管理、对象模型、异常、并发等
  • 有些工作由编译器直接生成代码,有些由运行时库提供
  • 语言越好用,往往运行时越“重”

正文

  一路走到这里,编译器似乎已经把所有事都做完了:读懂、优化、生成机器码、链接。可高级语言用起来那么“舒服”——你不用手动管理内存、不用关心对象在哪、还能随手抛个异常——这些便利,总得有人买单。

  买单的,就是语言运行时(language runtime)。

高级语言的“甜”是谁给的

  想一想,哪些事你从没亲手做过:

  • 申请和释放对象的内存
  • 检查数组越界
  • 抛出和捕获异常
  • 记录每个对象的类型
  • 支持多线程

  这些都不是“凭空”发生的。它们要么由编译器在生成的代码里插入检查与调用,要么由一组运行时库在程序运行时提供服务。这整套支撑,就是语言运行时。

编译器和运行时怎么分工

  两者的分工大致是这样:

  • 编译器:能静态确定的,就直接生成代码(比如类型检查、部分内存布局)
  • 运行时:需要“运行时才知道”的,交给运行时库(比如动态分配、垃圾回收、动态派发、异常展开)

  两者配合,才让高级语言既好用又能跑。

越“甜”,越“重”

  这里有一条很有意思的规律:语言提供的便利越多,运行时往往越庞大、越复杂。

  比如带自动垃圾回收的语言,需要一整套垃圾回收器;支持反射、动态派发的,要维护类型信息;有异常机制的,要维护异常处理数据。相比之下,贴近底层的语言,运行时可能非常小。

  这又回到了那个熟悉的味道:没有免费的便利。 语言帮你做的事越多,背后替你干活的运行时就越重。接下来几章,就看几项典型的运行时服务。

思考题 1

  语言运行时大致负责哪些工作?

思考题 2

  为什么说“语言越好用,运行时往往越重”?

小结

知识点

  • 语言运行时提供高级语言背后的隐藏服务
  • 职责包括内存管理、对象模型、异常、并发等
  • 编译器生成部分代码,运行时库提供其余服务
  • 语言特性越丰富,运行时通常越大

参考资料

  1. Wikipedia(zh):运行时系统:程序运行期间提供支持的系统
  2. Wikipedia(zh):垃圾回收:运行时自动管理内存的机制

思考题答案(仅供参考)

思考题 1

  它负责程序运行时需要的各种支持,例如内存分配与垃圾回收、对象与类型信息的维护、异常处理、动态方法派发、线程与并发支持等。部分是编译器插入的代码,部分是运行时库提供的服务。

思考题 2

  因为语言提供的每项便利通常都需要运行时的支撑:自动内存管理要有垃圾回收器,异常机制要有异常处理数据,动态派发要有类型信息。便利越多,需要的运行时服务就越多,运行时也就越大越复杂。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪