9

这个 SO thread中,我了解到seq在大型集合上保留对 a 的引用将防止整个集合被垃圾收集。

首先,该线程来自 2009 年。在“现代”Clojure(v1.4.0 或 v1.5.0)中仍然如此吗?

其次,这个问题是否也适用于惰性序列?例如,是否(def s (drop 999 (seq (range 1000))))允许垃圾收集器淘汰999序列的第一个元素?

最后,对于大型收藏品是否有解决此问题的好方法?换句话说,如果我有一个包含 1000 万个元素的向量,我是否可以以这样一种方式使用该向量,使得消耗的部分可以被垃圾收集?如果我有一个包含 1000 万个元素的哈希图呢?

我问的原因是我正在对相当大的数据集进行操作,并且我必须更加小心不要保留对对象的引用,以便可以对我不需要的对象进行垃圾收集。java.lang.OutOfMemoryError: GC overhead limit exceeded事实上,在某些情况下我会遇到错误。

4

2 回答 2

7

通常情况下,如果您“抓住序列的头部”,那么 Clojure 将被迫将所有内容保存在内存中。它没有选择:您仍然保留对它的引用。

然而,“达到 GC 开销限制”与内存不足错误不同——这更有可能表明您正在运行一个虚构的工作负载,该工作负载创建和丢弃对象的速度如此之快,以至于欺骗 GC 认为它超载。

看:

如果您对正在处理的项目施加实际工作量,我怀疑您会发现此错误不会再发生。在这种情况下,您可以轻松处理大于可用内存的惰性序列。

然而,像向量和哈希图这样的具体集合是另一回事:它们不是惰性的,因此必须始终完全保存在内存中。如果您的数据集大于内存,那么您的选择包括:

  • 使用惰性序列,不要抓住头部
  • 使用支持延迟加载的专用集合(我相信 Datomic 使用了一些这样的结构)
  • 将数据视为事件流(使用 Storm 之类的东西)
  • 编写自定义代码将数据分成块并一次处理一个。
于 2013-02-22T00:05:04.607 回答
0

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

至于解决方法,是的!lazy-seq 实现可以 gc 已经“处理”并且没有从绑定中直接引用的部分。只要确保你没有抓住序列的头部。

于 2013-02-21T18:04:11.007 回答