什么时候应该避免在Clojure中使用`seq`?

时间:2013-02-21 14:59:25

标签: memory memory-management clojure garbage-collection

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错误。

2 个答案:

答案 0 :(得分:6)

总是如此,如果你“抓住”序列的头部,那么Clojure将被迫将所有内容保存在内存中。它没有选择:你仍然保留对它的引用。

然而,“达到的GC开销限制”与内存不足错误不同 - 它更可能表明您正在运行虚构的工作负载,即创建和丢弃对象的速度太快以致于将GC引入以为它超载了。

请参阅:

如果您在正在处理的项目上放置实际工作负载,我怀疑您将看到此错误不再发生。在这种情况下,您可以轻松处理大于可用内存的延迟序列。

像向量和散列图这样的具体集合是另一回事:它们不是懒惰的,所以必须始终完全保存在内存中。如果您的数据集大于内存,那么您的选项包括:

  • 使用延迟序列而不是抓住头部
  • 使用支持延迟加载的专用集合(Datomic使用这样的结构,我相信)
  • 将数据视为事件流(使用类似Storm的方式)
  • 编写自定义代码以将数据分区为块并一次处理一个。

答案 1 :(得分:0)

如果你在一个绑定中保持一个序列的头部,那么你是正确的,它不能被gc'd(并且每个版本的Clojure都是如此)。如果您正在处理大量结果,为什么还需要保持头部?

至于它的方式,是的! lazy-seq实现可以gc已经“处理”但未在绑定中直接引用的部分。只要确保你没有抓住序列的头部。