OCaml 如何在内存中表示值

原标题How OCaml Represents Values in Memory

借助 OCaml 的 Obj 模块,实际了解立即整数、堆块、运行时标签和变体构造器。

频道
Jane Street
发布
2024年12月20日
视频
12:43
阅读
4 分钟
幻灯片
5
原文
英语
在 YouTube 打开
往下滚动阅读时,视频会缩到右下角继续播放。字幕跟随阅读语言。
阅读语言共 2 种语言可读
由 AI 从英语原文翻译。打开“对照原文”可以逐段核对。
摘要

30 秒看懂

一位 Jane Street 语言团队工程师介绍 OCaml 的常规值布局。通过交互式示例,讲者展示了带标签的整数、块头和标签如何帮助运行时与垃圾回收器区分值并理解堆块。

  1. 1立即值Unit 和 false 表示为整数 0,true 表示为 1,字符则用其字节码表示。
  2. 2用一位区分值对齐指针的最低位为 0;立即整数的最低位为 1,在展示的布局中留下 63 位用于整数表示。
  3. 3块头块头记录块的大小、垃圾回收器颜色和标签。
  4. 4原始数据的特殊标签装箱浮点数和字符串使用特殊标签,防止其内容被当作普通指针扫描。
  5. 5变体使用两套编号不带载荷的构造器用立即整数表示;带载荷的构造器则使用块标签。

要点建议

  1. Unit、布尔值、字符和整数使用立即整数表示。
  2. 在示例中,元组和记录等复合结构由堆块表示。
  3. 在展示的 64 位布局中,最低位用于区分立即整数与对齐的块指针。
  4. 块头包含大小、垃圾回收器颜色位和标签。
  5. 标签既能区分装箱浮点数和字符串等不可扫描块,也能标识带载荷的变体构造器。
  6. 不带载荷和带载荷的变体构造器分别独立编号。
全文

按章节阅读整理后的全文

只看原始文稿
01

立即值与复合结构

讲者介绍立即值,并用 Obj 检查它们的表示。数组、元组和记录则用于展示装箱块。

我在 Jane Street 的 OCaml 语言团队工作。这个系列之前的视频讨论过局部性等模式;另一个开发方向是 kind 系统,它可以表达包括内存布局在内的属性。在讨论无装箱类型之前,我想先解释 OCaml 的常规值表示。

第一类是立即值。Unit 只有一个值,写作 (),通常用于执行某种副作用而不返回有意义信息的计算。布尔值、字符和有符号整数也属于立即类型。

这些值都表示为 OCaml 整数。为了检查它们,我编写了一个辅助函数:先检查值的表示,再使用不安全的重新解释操作 Obj.magic,将其看作整数。Unit 和 false 表示为 0,true 表示为 1;字符对应其字节码,整数则对应自身。

检查立即值表示
幻灯片 1 / 5检查立即值表示REPL 定义了一个使用 Obj.repr 和 Obj.magic 的辅助函数,并检查 unit 值。

值也可以组合成结构:列表或数组可以包含同一类型的元素,元组可以包含不同类型的元素,记录则可以为各自拥有类型的字段命名。与立即类型不同,这些复合值经过装箱,由堆上分配的块表示。

第二个辅助函数使用 Obj.repr 查看运行时表示,检查它是否为块,然后调用 Obj.size。在这些例子中,包含四个元素的数组大小为四个机器字,三元组为三个,两字段记录为两个。Obj.repr 是恒等操作,让我们能够检查其表示。

02

带标签的整数与块头

垃圾回收器必须识别值及其大小。对齐指针、整数最低位标签和块头提供了这些信息。

运行时如何区分整数和指针,又如何确定块的大小?这不仅关系到检查操作:垃圾回收器也必须区分立即值和引用,并了解引用所指向的内存。

OCaml 使用带标签的整数。在这里展示的 64 位布局中,堆块按机器字对齐,因此地址末尾有若干个零位。运行时将最低有效位作为区分标记:1 表示立即整数,0 表示指针。这样就留下 63 位用于整数表示。

装箱结构的大小
幻灯片 2 / 5装箱结构的大小Obj.size 报告数组、元组和记录块以机器字为单位的大小。

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

块头
幻灯片 3 / 5块头图示展示了头部机器字中的大小、颜色和标签字段。
03

浮点数、字符串与标签

原始浮点数和字符串数据需要特殊处理,以免其位模式被解释为指针。块头标签用于标识这些不可扫描的块。

64 位浮点值无法总是留出一位来与指针区分。在普通装箱表示中,浮点数存储在一个堆块里,其标签为 Double_tag,即 253。垃圾回收器识别该标签,不会把浮点数的位模式当作指针扫描。

我可以对浮点数使用 Obj.repr 和 Obj.tag 来验证:报告的标签为 253。字符串也有类似问题,因为紧密排列的字节可以包含任意位模式。字符串块使用标签 252,因此其内容不会被误认为指针。

检查浮点数和字符串标签
幻灯片 4 / 5检查浮点数和字符串标签Obj.tag 显示装箱浮点数和字符串值的特殊标签。

更一般地说,标签大于或等于 251 的块,其内容不会被垃圾回收器当作普通的含指针字段扫描。标签还有另一个重要用途:区分变体类型的构造器。

04

变体构造器

OCaml 分别对不带载荷和带载荷的构造器编号。前者使用立即整数,后者使用块标签。

考虑一个变体,包含构造器 A、B of int、C 和 D of string。表示某个值时,运行时既需要知道构造器的身份,也需要知道任何关联载荷。OCaml 将不带参数的构造器和带参数的构造器分组,分别从零开始编号。

不带载荷的构造器是立即整数。在这个例子中,A 为 0,C 为 1;再有一个不带载荷的构造器就会是 2。对于带载荷的构造器,块标签记录构造器编号:B 的标签为 0,D 的标签为 1。

变体构造器的表示
幻灯片 5 / 5变体构造器的表示示例定义了带载荷和不带载荷的构造器,并检查立即值。

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

同频道的其他文章

1:48:29EN → 简体中文
JSJane Street· 2026年10月16日AI 如何改变编程:John Krazit 与 Ron Minsky 的对话Ron Minsky, John Krazit · Jane StreetJane Street 的 AI 工具负责人和 Signals and Threads 主持人探讨智能体基础设施、安全、代码审查,以及软件开发体验的变化。编程智能体Developer productivity评估+2
9 张幻灯片1:05:45EN → 简体中文
JSJane Street· 2026年9月11日Cyrus Omar:实时编程与人与 AI 共享的计算公共空间Cyrus Omar通过 Hazel 演示,探讨即时反馈、结构化协作和精细权限如何帮助人类与 AI 智能体共同工作。Programming LanguagesHuman-AI Collaboration开发流程与测试+2
1:25:00EN → 简体中文
JSJane Street· 2026年9月2日Eric Mannes:如何将杂乱的外部数据用于交易研究Ron, Eric Mannes · Jane Street从商品期货和 Excel 风险系统,到时点数据集和 AI,Eric Mannes 解释 Jane Street 数据工程背后的领域知识。Data quality & curation开发流程与测试Investing+2
7 张幻灯片1:16:29EN → 简体中文
JSJane Street· 2026年8月26日Grant Sanderson 与 Alok Puranik:从群论看位置编码Grant Sanderson, Alok Puranik · 3Blue1Brown从注意力机制的假设出发,他们推导矩阵指数,并将旋转、衰减和 Jordan 块与 RoPE、ALiBi 联系起来。Transformers & attentionMathematics机器学习基础+1
4:41EN → 简体中文
JSJane Street· 2026年7月6日走进 Jane Street 实习项目:工作、导师与学习Cody · Jane Street实习生和员工讲述协作式面试、从零学习金融,以及参与公司实际使用的项目的经历。CareersCompany CultureMachine Learning+1
1:34:29EN → 简体中文
JSJane Street· 2026年6月24日Jacob Baskin:构建 Superstore 与调度 HiveYaron Minsky, Jacob Baskin · Jane Street这位 Jane Street 工程师回顾分布式分析数据库背后的设计选择,以及为不断扩大的研究集群分配资源的挑战。基础设施与部署Software engineeringDecision Making+1