阅读最近的一个问题,我确定了正在讨论的功能
(def fib-seq
(lazy-cat [0 1] (map + (rest fib-seq) fib-seq)))
就像捕获一个序列的头部一样,但是我重新阅读我的答案时突然想到我已经掩盖了细节,就像它们很明显一样,所以我回去澄清并提出不足。我知道 fib-seq 是一个 var,只要它在它周围,它就会保存序列中的所有元素,但我完全不清楚序列是如何被保持的确切机制。任何澄清将不胜感激。
最佳答案
基本上,常规的 GC 规则适用......序列只是一个对象,持有它的头部意味着持有对该对象的引用。这需要在内存中保存尽可能多的序列,因为 Clojure 序列是缓存的。
(下面是更详细的解释——请参阅粗体片段以了解其要点...... ;-))
Clojure 中的“序列”是一个实现 ISeq 接口(interface)的对象。这提供了提取序列的第一个元素和序列的其余部分(另一个实现 ISeq 的对象)的方法。作为一个关键细节,这些不仅要计算正确的对象(序列的第一个/其余部分)并将其返回给调用者,而且还要将计算值缓存在内存中,以便任何后续请求更快——而且,更多重要的是,即使 ISeq 是在某个时刻发生变化的可变 Java 对象之上生成的,也可以保证对序列中相同元素的所有请求都返回相同的值。 (请注意,这对于 Clojure 序列的不可变语义绝对至关重要。)
另一方面,Var 是一个容器,粗略地说,它包含一个指向某个 Java 对象的“指针”。如果这恰好是一个 ISeq,那么 只要 Var 本身没有被垃圾回收 (如果它是当前存在的命名空间中的顶级 var,那么它显然永远不会是)或反弹,ISeq 本身不会被垃圾收集,特别是它用于缓存第一个/其余序列的内存不会被释放 .
至于序列的其他元素:绑定(bind)到 Var 的 ISeq 的“其余部分”是一个 ISeq 本身。此外,它被第一个 ISeq 缓存。因此,绑定(bind)到 Var 的 ISeq 的“剩余”ISeq 的第一个元素永远不会被垃圾收集,因为对它的引用被绑定(bind)到 Var 的 ISeq 的“剩余”ISeq 持有,而这个 ISeq 不会被 GC 处理,因为它被绑定(bind)到 Var 的 ISeq 缓存为“其余”组件,只要它绑定(bind)到 Var,它就不会被 GC,而这反过来通常永远不会被GC 是因为它是命名空间中的顶级 Var。
显然,如果 Var 不再被其命名空间( ns-unmap
)或命名空间本身被丢弃( remove-ns
),它将被 GC 处理。如果它碰巧持有一个 ISeq,那么当且仅当它没有被其他一些代码持有时,该 ISeq 才会被 GC——当然,通常的 GC 规则适用。对于 binding
引入的绑定(bind)let
引入的本地绑定(bind),以上所有都适用于绑定(bind)的模生命周期问题。 (这不是这个 Q 的主题。)
关于clojure - 捕获序列的头部,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2214258/