保持在序列的头部 [英] Holding onto the head of a sequence

查看:141
本文介绍了保持在序列的头部的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

阅读最近的一个问题我确定了正在讨论的功能

 (def fib-seq 
(lazy-cat [0 1](map +(rest fib-seq)fib-seq)))

在序列的头部,但我发现我重新阅读我的答案,我已经掩饰了细节,因为它们是显而易见的,所以我回去澄清和来了短。我知道fib-seq是一个var,只要它在它周围,它将包含序列中的所有元素,但我完全不清楚如何确切的序列被保持到的确切机制。

解决方案

基本上,常规GC规则适用...序列只是一个对象,头部意味着保持对该对象的引用。这需要保持与已经在内存中实现的一样多的序列,因为Clojure序列被缓存。



(更详细的解释如下 - Clojure中的一个序列是一个实现ISeq接口的对象。这提供了提取序列的第一元素和序列的其余部分(实现ISeq的另一对象)的方法。作为一个关键的细节,这些注意不仅计算正确的对象(序列的第一/休息),并将其返回给调用者,而且缓存计算的值在内存中,所以任何后续的请求更快 - 更多重要的是,即使ISeq正在一个可变的Java对象的顶部生成,而该对象在某个时刻发生变化,那么对序列中同一个元素的所有请求都会保证返回相同的值。 (注意这对于Clojure序列的不可变语义是绝对重要的。)



另一方面,Var是一个容器,指针到一些Java对象。如果这恰好是一个ISeq,则只要Var本身不是垃圾收集(如果它是一个当前存在的命名空间中的顶级var,它显然不会是)或反弹,ISeq本身不会被垃圾收集,并且特别地,它用于缓存第一/其余序列的存储器将不被释放



对于序列的其他元素:绑定到Var的ISeq的rest是ISeq本身。此外,它被第一个ISeq缓存。因此,绑定到Var的ISeq的'rest'ISeq的第一个元素将永远不会被垃圾收集,因为对它的引用由绑定到Var的ISeq的'rest'ISeq保持,并且此ISeq不会因为它被绑定到Var的ISeq缓存为'rest'组件,而这又不会被GC'd,只要它绑定到Var,而这又将通常永远不会GC',因为它是命名空间中的顶级Var。



很明显,如果不再被保存,Var ns-unmap )或命名空间本身被抛出( remove-ns )。如果它恰巧持有一个ISeq,那么当且仅当它没有被其他一些代码所占用时,ISeq才会被GC' - 当然,通常的GC规则适用。对于用 binding 引入的绑定以及用 let 引入的本地绑定,上述都适用于绑定的模数生命周期问题。 (这不是此问题的主题。)


Reading a recent question I identified the function being discussed

(def fib-seq
    (lazy-cat [0 1] (map + (rest fib-seq) fib-seq)))

as holding onto the head of a sequence, but it occurred to me re-reading my answer that I had glossed over the details like they were obvious, so I went back to clarify and came up short. I know that fib-seq is a var and that as long as it's around it will hold all the elements in the sequence, but I'm not clear at all on the exact mechanics of how exactly the sequence is getting held onto. Any clarifications would be appreciated.

解决方案

Basically, regular GC rules apply... A sequence is just an object and holding onto its head means holding onto a reference to this object. This entails holding as much of the sequence as has already been realised in memory, because Clojure sequences are cached.

(A more detailed explanation follows -- see the fragment in bold for the gist of it... ;-))

'A sequence' in Clojure is an object which implements the ISeq interface. This provides methods which extract the first element of the sequence and the rest of the sequence (another object implementing ISeq). As a key detail, these take care not only of computing the correct object (first / rest of the sequence) and returning it to the caller, but also of caching the computed value in memory so any subsequent requests are faster -- and, more importantly, so that all requests for the same element of the sequence are guaranteed to return the same value, even if the ISeq is being generated on top of a mutable Java object which changes at some point. (Note this is absolutely crucial to the immutable semantics of Clojure sequences.)

A Var, on the other hand, is a container which holds, in rough terms, a 'pointer' to some Java object. If this happens to be an ISeq, then as long as the Var itself is not garbage collected (which it obviously won't ever be if it's a top level var in a currently existing namespace) or rebound, the ISeq itself will not be garbage collected and, in particular, the memory it uses for caching first / rest of sequence will not be released.

As for the other elements of the sequence: the 'rest' of the ISeq bound to the Var is an ISeq itself. Also, it gets cached by the first ISeq. Thus the first element of the 'rest' ISeq of an ISeq bound to a Var will never be garbage collected, because a reference to it is being held by the 'rest' ISeq of the ISeq bound to the Var and this ISeq won't be GC'd because it is being cached as the 'rest' component by the ISeq bound to the Var, which in turn won't be GC'd as long as it is bound to the Var, which in turn will normally never be GC'd because it's a top-level Var in a namespace.

Clearly the Var will be GC'd if it ceases to be held onto by its namespace (ns-unmap) or the namespace itself gets tossed (remove-ns). If it happens to have held an ISeq, that ISeq will be GC'd if and only if it isn't being held by some other bit of code -- the usual GC rules apply, of course. For bindings introduced with binding and local bindings introduced with let, all the above applies modulo lifetime issues of the bindings. (Which are not a subject of this Q.)

这篇关于保持在序列的头部的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆