Clojure:使用 eval 的内存泄漏

Clojure: memory leaks using eval

我的应用程序评估从远程客户端接收到的引用表达式。随着时间的推移,我的系统内存增加并最终崩溃。我发现的是:

当我在 docker 容器中从 Clojure 的 nrepl 执行以下代码时:

(dotimes [x 1000000] ; or some arbitrary large number
 (eval '(+ 1 1)))

容器的内存使用量不断上升,直到达到极限,此时系统将崩溃。

如何解决这个问题?
还有另一个 thread 提到了这种行为。其中一个答案提到使用tools.reader,如果我需要执行代码,它仍然使用eval,导致同样的问题。

没有简单的方法来解决这个问题,因为每次调用 eval 都会创建一个新的 class,即使您正在评估的表单完全相同。 JVM 本身不会删除新的 classes.

有两种方法可以避免这种情况:

  • 完全停止使用 eval(例如,通过创建您自己的 DSL 或您自己的功能有限的 eval 版本)或至少减少使用它的频率,例如通过批处理您需要评估的表格
  • 卸载已加载 classes - 我自己还没有完成,可能需要做很多工作,但您可以按照本主题中的答案进行操作:Unloading classes in java?

我不知道 eval 内部是如何工作的, 但根据我的观察,我认为你的结论是不正确的,而且 Eugene 的评论“就其本身而言,JVM 不会摆脱新的 类”似乎是错误的。

I run your sample with -Xmx256m and it went fine.

  (time (dotimes [x 1000000] ; or some arbitrary large number
          (eval '(+ 1 1))))
  ;; "Elapsed time: 529079.032449 msecs"

我检查了 the question you linked,他们说正在增长的是 Metaspace,而不是堆。 所以我观察了 Metaspace,它的使用率在增长,但 也在缩减

你可以在这里找到我的实验以及来自 JMC 控制台的一些图表:https://github.com/jumarko/clojure-experiments/commit/824f3a69019840940eaa88c3427515bcba33c4d2

注意:为了 运行 这个实验,我在 macOS

上使用了 JDK 17.0.2