我有一个Clojure程序在运行时消耗了大量的堆(我曾经在2.8GiB附近测量它),我正试图找到一种方法来减少其内存占用。我目前的计划是每隔一段时间强制进行垃圾收集,但我想知道这是不是一个好主意。我已阅读How to force garbage collection in Java?和Can I Force Garbage Collection in Java?并了解如何这样做 - 只需致电(System/gc)
- 但我不知道这是不是一个好主意,或者即使它是必要的。
以下是程序的工作原理。我有大量遗留格式的文档,我正在尝试将其转换为HTML。遗留格式由几个XML文件组成:描述文档的元数据文件,包含指向任意数量内容文件的链接(通常是一个,但也可以是几个 - 例如,某些文档具有“主要”内容和单独的脚注文件)。对于最小的文档,转换需要几毫秒到最大文档的大约58秒。基本上,我正在编写一个美化的XSLT处理器,尽管用的是很多更好的语言而不是XSLT。
当我刚刚在Clojure开始编写时,我当前(相当幼稚)的方法构建了所有元数据文件的列表,然后执行以下操作:
(let [parsed-trees (map parse metadata-files)]
(dorun (map work-func parsed-trees)))
work-func
将文件转换为HTML并将结果写入磁盘,返回nil
。 (我试图丢弃每个文档的解析后的XML树,这在每次通过单个文档后非常大)。我现在意识到尽管map
是懒惰的,而dorun
抛弃了它迭代的序列的头部,我抓住parsed-trees
中seq头部的事实就是为什么我失败了。
我的新计划是将解析移到work-func
,以便它看起来像:
(defn work-func [metadata-filename]
(-> metadata-filename
e/parse
xml-to-html
write-html-file)
(System/gc))
然后我可以用work-func
或map
来调用pmap
,因为我有两个双核CPU,并且希望在处理完每个文档后丢弃大型XML树。 / p>
我的问题是:经常告诉Java“请跟我清理”是一个好主意吗?或者我应该跳过(System/gc)
中的work-func
调用,并在感觉需要时让Java垃圾收集器运行?我的直觉是要保持调用,因为我知道(正如Java不可能)在work-func
的那一点,堆上会有大量的数据可以被删除,但我欢迎来自更有经验的Java和/或Clojure程序员的意见。
答案 0 :(得分:10)
调用System/gc
并非有用的策略。假设您现在无法减少代码的实际内存占用量,您应该确保避免主要GC 。这将自动发生(通过调整Young Generation的大小直到所有临时数据都适合),或者您可以使用明确的JVM选项对其进行调整,以使YG异常大。
只要你的短命物体因为空间不足而不能溢出老一代,你就会遇到非常短暂的GC暂停。您也不必担心显式调用GC:它会在Eden Space填满后立即发生。