阅读最近的问题,我确定了正在讨论的功能
(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,显然不会出现),就可以或反弹,只要eqt_strong或反弹,ISeq本身就可以不会被垃圾收集,特别是,它用于缓存优先级/其余序列的内存不会被释放。
至于序列的其他元素:绑定(bind)到Var的ISeq的“其余部分”本身就是ISeq。此外,它会被第一个ISeq缓存。因此,绑定(bind)到Var的ISeq的“剩余” ISeq的第一个元素将永远不会被垃圾回收,因为绑定(bind)到Var的ISeq的“剩余” ISeq保留了对该元素的引用,而该ISeq不会之所以将其设为GC,是因为绑定(bind)到Var的ISeq被作为“剩余”组件缓存,而只要绑定(bind)到Var,ISeq就不会被GC。使用GC,因为它是 namespace 中的顶级Var。
显然,如果Var不再由其 namespace (ns-unmap
)保留,或者 namespace 本身被抛弃(remove-ns
),它将被GC处理。如果恰好持有ISeq,则该ISeq将在且仅当不由其他代码保留时才被GC-当然,通常适用GC规则。对于由binding
引入的绑定(bind)和由let
引入的本地绑定(bind),以上所有内容都应用了绑定(bind)的模数生命期问题。 (这不是此Q的主题。)