我正在尝试
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))
但这也在同一点上停止增长.
这怎么没爆炸?特别是因为我使用了严格的地图,它应该保留在内存中的一切不应该吗?就它“知道”而言,我希望它能在某些时候打印整个列表,所以它需要保留所有内容.另外,如果它在某些时候解除分配,那么被覆盖的终结方法是否应该克服这一点?
谁能解释一下这里发生了什么?我在休息时写了这篇文章,所以我可能会忽略一些东西,但我看不清楚是什么.
它在Intellij / Cursive的REPL中进行了测试.
最佳答案 你的JVM基本上花费了整个垃圾收集时间.在我的系统上,默认堆大小的限制是~8.7GB,所以根据这给你的不同,你的GC会更早地试图保持低于此值:
java -XX:+PrintFlagsFinal -version | grep HeapSize
您可以使用-Xmx JVM选项增加此值.
您的程序实际上将继续运行并继续向您的向量添加内容.我建议使用以下选项启动JVM:XX:PrintGCDetails以查看发生了什么.在接近堆限制之后,JVM将在几秒钟内执行Full GC.注意,那个if the JVM spends too much time for the GC then it will throw and OOM error.
要看到你还在分配,只需抛出(当(零?(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))'