30 秒看懂
一位 Jane Street 语言团队工程师介绍 OCaml 的常规值布局。通过交互式示例,讲者展示了带标签的整数、块头和标签如何帮助运行时与垃圾回收器区分值并理解堆块。
- 1立即值Unit 和 false 表示为整数 0,true 表示为 1,字符则用其字节码表示。
- 2用一位区分值对齐指针的最低位为 0;立即整数的最低位为 1,在展示的布局中留下 63 位用于整数表示。
- 3块头块头记录块的大小、垃圾回收器颜色和标签。
- 4原始数据的特殊标签装箱浮点数和字符串使用特殊标签,防止其内容被当作普通指针扫描。
- 5变体使用两套编号不带载荷的构造器用立即整数表示;带载荷的构造器则使用块标签。
要点建议
- Unit、布尔值、字符和整数使用立即整数表示。
- 在示例中,元组和记录等复合结构由堆块表示。
- 在展示的 64 位布局中,最低位用于区分立即整数与对齐的块指针。
- 块头包含大小、垃圾回收器颜色位和标签。
- 标签既能区分装箱浮点数和字符串等不可扫描块,也能标识带载荷的变体构造器。
- 不带载荷和带载荷的变体构造器分别独立编号。
按章节阅读整理后的全文
立即值与复合结构
讲者介绍立即值,并用 Obj 检查它们的表示。数组、元组和记录则用于展示装箱块。
我在 Jane Street 的 OCaml 语言团队工作。这个系列之前的视频讨论过局部性等模式;另一个开发方向是 kind 系统,它可以表达包括内存布局在内的属性。在讨论无装箱类型之前,我想先解释 OCaml 的常规值表示。
第一类是立即值。Unit 只有一个值,写作 (),通常用于执行某种副作用而不返回有意义信息的计算。布尔值、字符和有符号整数也属于立即类型。
这些值都表示为 OCaml 整数。为了检查它们,我编写了一个辅助函数:先检查值的表示,再使用不安全的重新解释操作 Obj.magic,将其看作整数。Unit 和 false 表示为 0,true 表示为 1;字符对应其字节码,整数则对应自身。

值也可以组合成结构:列表或数组可以包含同一类型的元素,元组可以包含不同类型的元素,记录则可以为各自拥有类型的字段命名。与立即类型不同,这些复合值经过装箱,由堆上分配的块表示。
第二个辅助函数使用 Obj.repr 查看运行时表示,检查它是否为块,然后调用 Obj.size。在这些例子中,包含四个元素的数组大小为四个机器字,三元组为三个,两字段记录为两个。Obj.repr 是恒等操作,让我们能够检查其表示。
带标签的整数与块头
垃圾回收器必须识别值及其大小。对齐指针、整数最低位标签和块头提供了这些信息。
运行时如何区分整数和指针,又如何确定块的大小?这不仅关系到检查操作:垃圾回收器也必须区分立即值和引用,并了解引用所指向的内存。
OCaml 使用带标签的整数。在这里展示的 64 位布局中,堆块按机器字对齐,因此地址末尾有若干个零位。运行时将最低有效位作为区分标记:1 表示立即整数,0 表示指针。这样就留下 63 位用于整数表示。

每个堆块还有一个额外的头部机器字。在展示的布局中,大小字段记录块以机器字为单位的长度;Obj.size 读取该字段。两个颜色位供垃圾回收器使用,一个字节的标签则向运行时提供更多关于块的信息。

浮点数、字符串与标签
原始浮点数和字符串数据需要特殊处理,以免其位模式被解释为指针。块头标签用于标识这些不可扫描的块。
64 位浮点值无法总是留出一位来与指针区分。在普通装箱表示中,浮点数存储在一个堆块里,其标签为 Double_tag,即 253。垃圾回收器识别该标签,不会把浮点数的位模式当作指针扫描。
我可以对浮点数使用 Obj.repr 和 Obj.tag 来验证:报告的标签为 253。字符串也有类似问题,因为紧密排列的字节可以包含任意位模式。字符串块使用标签 252,因此其内容不会被误认为指针。

更一般地说,标签大于或等于 251 的块,其内容不会被垃圾回收器当作普通的含指针字段扫描。标签还有另一个重要用途:区分变体类型的构造器。
变体构造器
OCaml 分别对不带载荷和带载荷的构造器编号。前者使用立即整数,后者使用块标签。
考虑一个变体,包含构造器 A、B of int、C 和 D of string。表示某个值时,运行时既需要知道构造器的身份,也需要知道任何关联载荷。OCaml 将不带参数的构造器和带参数的构造器分组,分别从零开始编号。
不带载荷的构造器是立即整数。在这个例子中,A 为 0,C 为 1;再有一个不带载荷的构造器就会是 2。对于带载荷的构造器,块标签记录构造器编号:B 的标签为 0,D 的标签为 1。

由于标签的取值有限,一个变体中带载荷的构造器数量也有上限,不过这个限制对大多数用途来说已经足够。这种直接的表示方式让我们能够推理许多类型的布局,也有助于保持运行时和垃圾回收器相对简单。后续视频会讨论这些值类型如何与无装箱类型相互作用。
同频道的其他文章
1:48:29EN → 简体中文
1:05:45EN → 简体中文
1:25:00EN → 简体中文
1:16:29EN → 简体中文
4:41EN → 简体中文
1:34:29EN → 简体中文