我正在尝试this challenge,这需要你编写一个不断消耗内存的程序。
我认为这很容易,所以我写了:
(reduce conj [] (range))
这基本上将无限范围内的数字添加到矢量中;理论上永远。
问题是,这会跳到3 / 4GB的内存,然后停止。 CPU仍在努力运行,但它拒绝增长。
我决定从Java答案中窃取一个想法并覆盖finalize
以在每次解除分配时创建更多对象:
(defrecord A []
Object
(finalize [this] (mapv (fn [_] (A.)) (range 5)))) ; Create 5 more
(mapv (fn [_] (A.)) (range))
但这也会在同一点上停止增长。
这怎么没爆炸?特别是因为我使用严格的map
,它应该保留在记忆中的所有内容吗?至于它已经知道",我希望它在某个时刻打印整个列表,所以它需要抓住一切。另外,如果它在某个时候解除分配,那么被覆盖的finalize
方法是否应该克服这个问题?
有人能解释一下这里发生了什么吗?我在休息时写了这篇文章,所以我可能会忽略一些东西,但我无法看到。 它在Intellij / Cursive的REPL中进行了测试。
答案 0 :(得分:2)
您的JVM基本上花费了整个垃圾收集时间。在我的系统上,默认堆大小的限制是~8.7GB,所以根据这给你的不同,你的GC将会更早地保持低于此值:
java -XX:+PrintFlagsFinal -version | grep HeapSize
您可以使用-Xmx
JVM选项增加此功能。
您的程序实际上会继续运行并继续向您的向量添加内容。我建议使用选项:XX:+PrintGCDetails
启动JVM以查看正在进行的操作。在接近堆限制之后,JVM将在几秒钟内执行Full GC。请注意,if the JVM spends too much time for the GC then it will throw and OOM error。
要查看您仍在分配,只需将(when (zero? (rem x 1e5)) (println x))
投入:
java -server -Xmx2g -XX:+PrintGCDetails -cp clojure-1.8.0.jar clojure.main -e '(mapv #(cond-> % (zero? (rem % 1e4)) println) (range))'