在this SO thread中,我了解到在大型集合上保留对seq
的引用将阻止整个集合被垃圾收集。
首先,该线程来自2009年。在“现代”Clojure(v1.4.0或v1.5.0)中,这仍然是正确的吗?
其次,这个问题是否也适用于懒惰序列?例如,(def s (drop 999 (seq (range 1000))))
是否允许垃圾收集器淘汰序列的第一个999
元素?
最后,对于大型集合,是否有一个解决此问题的好方法?换句话说,如果我有一个1000万个元素的向量,我可以消耗这个向量,以便消耗的部分可以被垃圾收集吗?如果我有一个包含1000万个元素的hashmap呢?
我问的原因是我在相当大的数据集上运行,并且我必须更加小心不要保留对象的引用,以便我不需要的对象可以被垃圾收集。实际上,我在某些情况下遇到java.lang.OutOfMemoryError: GC overhead limit exceeded
错误。
答案 0 :(得分:6)
总是如此,如果你“抓住”序列的头部,那么Clojure将被迫将所有内容保存在内存中。它没有选择:你仍然保留对它的引用。
然而,“达到的GC开销限制”与内存不足错误不同 - 它更可能表明您正在运行虚构的工作负载,即创建和丢弃对象的速度太快以致于将GC引入以为它超载了。
请参阅:
如果您在正在处理的项目上放置实际工作负载,我怀疑您将看到此错误不再发生。在这种情况下,您可以轻松处理大于可用内存的延迟序列。
像向量和散列图这样的具体集合是另一回事:它们不是懒惰的,所以必须始终完全保存在内存中。如果您的数据集大于内存,那么您的选项包括:
答案 1 :(得分:0)
如果你在一个绑定中保持一个序列的头部,那么你是正确的,它不能被gc'd(并且每个版本的Clojure都是如此)。如果您正在处理大量结果,为什么还需要保持头部?
至于它的方式,是的! lazy-seq实现可以gc已经“处理”但未在绑定中直接引用的部分。只要确保你没有抓住序列的头部。