Haskell 运行时如何区分指针和未装箱的字大小的值?

How does the Haskell runtime distinguish between pointers and unboxed word-sized values?

在 64 位平台上,由于指针标记,OCaml 的 int 类型是 63 位。这允许 int 被拆箱并且在运行时仍然可以与指针区分开来,从而允许精确的 GC。 IIRC,GHC RTS 中的 GC 也很精确,但 GHC 的 Int 是 64 位的,可以拆箱。如果是这样,那么运行时系统如何区分 Int 和指针?似乎在区分其他未装箱的字大小的值和指针时也会出现同样的问题。

简短的版本是:分配值时将所有指针和所有非指针组合在一起,并包含一些元数据,以便 GC 知道要遵循什么。

请注意,Haskell 报告实际上允许 Int 为 31 位或 63 位,从而使 OCaml 策略有效——但这不是 GHC 所做的。

稍长的版本是说"metadata"实际上是垃圾收集器使用的几个函数。为了给出一个粗略的草图,您可以将 Haskell 中的值视为在运行时表示为具有以下方法的 OO 样式对象:

class Fn:
    # By far the most used; this evaluates the value:
    enter(...) -> ...
    # Used by the garbage collector:
    scavenge(...) -> ...
    evacuate(...) -> ...

这样做的结果是值对自己有足够的了解来进行簿记,并且对于一些常见的布局,GHC 定义了这些函数的专用版本;垃圾收集器可能几乎不知道清除和疏散是如何工作的。指针和非指针的分离是为了可以为常见情况创建和共享通用实现。

请注意,即使 Haskell 值不是 "functions,",'enter' 函数也存在,因为懒惰意味着即使类型是例如int,评估可能仍然涉及计算。

如果你想要很长的版本,我建议阅读:

https://www.microsoft.com/en-us/research/publication/implementing-lazy-functional-languages-on-stock-hardware-the-spineless-tagless-g-machine/

其中详细介绍了 Haskell 如何映射到硬件。这是一本引人入胜的读物,里面有很多巧妙的东西,与大多数(严格的)函数式语言的实现方式大不相同。这篇论文很旧,但仍然有意义。

本质上,这在 GHC RTS documentation 中有描述,它详细说明了堆 object 的格式,包括 header 和有效负载。

header描述了payload的哪些字是指针,这样垃圾collection就可以工作了

这意味着对于任何堆 object。

粗略地说,有一个字的开销