【问题标题】:How can I avoid running out of heap memory when processing vast sequences in Clojure?在 Clojure 中处理大量序列时,如何避免堆内存不足?
【发布时间】:2012-06-12 10:57:32
【问题描述】:

我正在尝试生成一个序列,该序列对应于一个非常宽、深的树的广度优先搜索……当我沿着​​序列走得太远时,我遇到了内存问题。在 IRC 频道上四处询问并查看这里,导致此类问题的第一大原因是不经意间抓住了头;但我看不到我在哪里做这个。

代码很简单;这是一个显示问题的版本:

(def atoms '(a b c))

(defn get-ch [n] (map #(str n %) atoms)) 

(defn add-ch 
  ([] (apply concat (iterate add-ch atoms))) 
  ([n] (mapcat get-ch n)))

(dorun (take 20000000 (add-ch)))

这是另一个版本(这是我在从#clojure 获得帮助之前开始使用的版本),它显示了相同的问题:

(def atoms '(a b c))

(defn get-children [n] (map #(str n %) atoms))

(defn add-layer 
  ([] (add-layer atoms)) 
  ([n] (let [child-nodes (mapcat get-children n) ] 
      (lazy-seq (concat n (add-layer child-nodes))))))

(dorun (take 20000000 (add-layer)))

两者都给了我一个“OutOfMemoryError Java 堆空间”。我在 Macbook Air 上从 Eclipse/CounterClockwise 中的 REPL 运行它们。

我对 Clojure 还是很陌生,所以在为此苦苦挣扎了一天之后,我希望这是我忽略的微不足道的事情。我意识到我可以提高堆大小以降低问题发生的可能性,但我最终要处理的序列非常庞大,我认为这对我没有帮助。

我尝试将“take”(在上面的示例中)替换为“drop”,以避免保持头部 - 这没有区别。

【问题讨论】:

  • 你的 JVM 内存选项怎么样? >= Xmx2g?
  • 这有助于降低问题的可能性,但它仍然会进一步发生,不是吗?

标签: memory clojure jvm sequence heap-memory


【解决方案1】:

我错过了dorun。 问题似乎出在 StringBuilder str 上。

如果我将 get-children 替换如下:

 (defn get-children [n] (map #(if (seq? n) (conj n %) (conj (list n) %)) atoms))

【讨论】:

  • 当 repl 尝试打印序列时,它会保持头部。一个常见的习惯用法是对巨大的惰性序列进行doseq 并一次打印一个元素。我有一个替代解决方案似乎可行 - 要点是用列表中的 conj 替换 str ,然后直接评估(取 200000 ...),使用 doseq 并打印。
  • 请忽略我之前的评论——我错过了dorun。使用上述 get-children 的替代实现,它避免了 str 确保此实现一直是惰性的。
  • 我说删除错误的东西。我会删除我的 q,然后没人会知道...(感谢您深入了解这个问题 - 有趣的问题,但我仍然不确定我是否理解修复程序修复问题的原因)
  • 恐怕对我不起作用 - 我仍然收到 OutOfMemoryError。我认为这里的问题实际上与 Clojure 无关:它与算法有关。我正在维护一系列条目;每次我取下一个,我都会添加它的孩子——所以这个序列会增加 2 个条目。最终,这必须溢出内存。我怀疑通过添加到现有序列而不是为每个条目创建一个新字符串,您的实现会持续更长时间。
  • 汤姆,你的内存用完了多少-我尝试了 200000000 并且我用完了堆栈,而不是堆-我怀疑那是因为递归调用-使用 recur 也可以解决这个问题.
猜你喜欢
  • 2022-08-05
  • 2016-03-23
  • 1970-01-01
  • 2012-05-13
  • 1970-01-01
  • 2020-03-26
  • 1970-01-01
  • 2015-06-15
  • 2013-10-04
相关资源
最近更新 更多