【问题标题】:How does OCaml GC decide which block in minor heap is live or notOCaml GC 如何决定次要堆中的哪个块处于活动状态
【发布时间】:2014-08-06 06:53:58
【问题描述】:

Chapter 21, Real World OCaml,上面写着:

次要堆是保存大部分短期值的地方。它 由一块连续的虚拟内存组成,其中包含一个 OCaml 块的序列。如果有空间,分配一个新块是 一个快速、恒定时间的操作,只需要几个 CPU 说明。

为了对次堆进行垃圾收集,OCaml 使用复制收集将次堆中的所有活动块移动到主堆。这需要 工作与次堆中的活动块数成正比, 根据世代假设,这通常很小。这 次要收集停止了世界(它停止了应用程序) 当它运行时,这就是为什么它如此重要以使其快速完成 让应用程序以最小的中断恢复运行。

我明白正如它所说的那样,在小堆中,实时内存进入主堆。

但是,gc 如何决定小堆中的实时内存?

主堆的标记和清除过程是否相同?

基本上,在 ma​​jor 堆的 gc 期间,它会在堆栈中启动一些根,并且任何可以到达的东西都是实时的。

如果minor的gc也是这种情况,那么OCaml如何快速知道哪些根导致了minor heap memory,或者快速知道哪些root导致了major memory?


我的困惑实际上是如果OCaml 对minor 和major 做相同的gc 操作,那么OCaml 怎么区分?如果都是从栈开始,GC怎么能只在minor上收集,不涉及major呢?


编辑

假设次要堆大小只有 4KB。这是一些代码:

let f = 
  let a1 = Array.make (4 * 1024) 1 in
  let a2 = Array.make (4 * 1024) 2 in
  let a3 = Array.make (4 * 1024) 3 in
  ...

在上面的代码中

  1. a1 的地址将被放入堆栈,真正的数组将在小堆中。堆栈看起来像 [a1]
  2. a2 正在创建中。它发现次要堆已满,因此将执行次要 gc。 gc 查看堆栈,因此跟随 a1 并确定 a1 处于活动状态,然后 a1's array 将被复制到主堆,并且 a1 的指针更改为主堆中的新地址?堆栈现在看起来像 [a2;a1]
  3. a3 正在创建中。就像2一样,它必须做一个小gc。 gc 会通过 a2 和 a1 吗?尤其是 a1,即使 a1 是大调?

【问题讨论】:

标签: garbage-collection ocaml


【解决方案1】:

主堆指向次堆的指针相对较少,因为主堆中的值较旧。如果 OCaml 是纯函数式的,就不会有任何从主要堆到次要堆的指针。

事实上,OCaml 并不是纯粹的函数式,主堆中的旧值可以用指向次堆中新值的指针来更新。任何这样的写入都会通过一个写屏障,从下一个小堆集合的角度来看,该屏障将主堆值记录为根。不需要为次要集合扫描主要堆的其余部分。

转到the book your are currently reading 中的“代际指针”部分,快速了解写屏障的实现。

关于你的例子:

是的,GC 访问堆栈以确定必须保留哪些次要堆值,但是在第 3 点,当 GC 看到堆栈中指向 a1 的指针指向次要堆之外时,它可以忽略它并立即继续a2。从主堆到次堆的所有引用都被写屏障收集在一个引用表中。不需要访问主堆中的其他位置,即使是堆栈中引用的位置。

gc 会通过 a2 和 a1 吗?

不,只有a2。没有理由访问a1,因为它在minor heap之外,因此不能指向minor heap,除非它被修改过,在这种情况下它将被单独注册到引用表中。

【讨论】:

  • There is no reason to visit a1 as it is outside the minor heap OCaml 怎么知道a1 不是未成年人? OCaml 运行时是否保留这样的表?
  • @JacksonTale 如果您考虑了两分钟,您将如何测试一个块是否在次要堆中?
  • 查看a1的地址是否在minor heap范围内?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-07
  • 2011-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多