我的clojure应用程序在运行时评估在单独的.edn
文件中定义的代码,即如果.edn
文件被更改,则包含的函数定义将重新加载到一个原子中,该原子经常用于计算。 / p>
如果它没有限制,应用程序似乎会在一段时间后填满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
使用的所有内存都已正确清理?
答案 0 :(得分:0)
你不应该使用eval
。它不适用于您的用例。请改用tools.reader
。
https://github.com/clojure/tools.reader
详细讨论:
http://www.learningclojure.com/2013/02/clojures-reader-is-unsafe.html?m=1
答案 1 :(得分:0)
我建议您保留"数据"与"执行"分开(EVAL)。 据推测,您在EDN文件(即数据)中更改的是关键字和数字。
那么如果您不是在主代码中定义操作而不是在数据文件中保留数据,那么该怎么办呢?如果您的数据还需要指定要执行的操作,您也可以使用关键字指示,然后让执行代码相应地执行操作。
保持数据和可执行文件分离对我来说既清晰又安全,包括允许您使用专用的EDN阅读器来读取和解析输入数据。