【发布时间】:2015-04-13 15:04:40
【问题描述】:
我的 clojure 应用程序在运行时评估在单独的 .edn 文件中定义的代码,即如果 .edn 文件发生更改,则包含的函数定义将重新加载到原子中,该原子经常用于计算。
如果没有限制,应用程序似乎会在一段时间后填满 JVM 的元空间(无边界)。堆空间使用正常。
我使用“YourKit”分析器来追踪泄漏。似乎大多数分配,包括分析器说“无法从垃圾收集器根中访问”的分配,都来自 eval 调用:
(defn eval-edn [e params_ dynparams_]
(let [input e
pairs (seq input)]
(binding [*ns* (find-ns 'myapp.core)
moods (:moods input)
last-moods @state/moods-atom
effects (:effects input)
last-effects @state/effects-atom
params params_
dynparams dynparams_
param-stats @state/coreparam-stats]
(eval input))))
其中params 和dynparams 是动态变量。
.edn 文件如下所示:
{:moods {:happiness
(* (:happiness:factor dynparams)
(* 0.5 (Math/sin (* (/ (- (:weather:temperature params 0) 10) 30)
Math/PI))))
; ...
}}
具体来说,内存快照将显示数千个 myapp.core$evalN.invoke() 类型的对象,其中 N 是每个新调用都会增加一些索引。
我已经读到应该尽可能避免使用eval,但我不明白如何在没有它的情况下在 JAR 可执行文件中实现在运行时重新加载代码。
如何确保eval 使用的所有内存都被正确清理?
【问题讨论】:
-
如果不解决内存泄漏本身,而不是将函数定义存储在自定义 EDN 结构中,为什么不将它们作为常规函数定义写在 .clj 文件中,并使用load-file?跨度>
-
我遇到了同样的问题。我的应用程序设置了一个安全测试器(带有 clojail),它最终评估用户提交的脚本。加载的类在每个 eval 上都会增长,因此元空间会随着时间的推移而填满。
标签: memory-leaks clojure