我正在使用mstor库来解析mbox邮件文件。有些文件的大小超过千兆字节。可以想象,这可能会导致一些堆空间问题。
有一个循环,对于每次迭代,都会检索特定的消息。 getMessage()
调用是在它耗尽时尝试分配堆空间的。如果我在这个循环的顶部添加对System.gc()
的调用,程序会毫无错误地解析大文件,但我意识到收集垃圾40,000次必须减慢程序的速度。
我的第一次尝试是让呼叫看起来像if (i % 500 == 0) System.gc()
,以使呼叫每500条记录发生一次。我尝试提高和降低此数字,但结果不一致,通常会返回OutOfMemory错误。
我的第二个更聪明的尝试是这样的:
try {
message = inbox.getMessage(i);
} catch (OutOfMemoryError e) {
if (firstTry) {
i--;
firstTry = false;
} else {
firstTry = true;
System.out.println("Message " + i + " skipped.");
}
System.gc();
continue;
}
这个想法是只在抛出OutOfMemory错误时调用垃圾收集器,然后递减计数再试一次。不幸的是,在解析了几千封电子邮件后,程序才开始输出:
Message 7030 skipped.
Message 7031 skipped.
....
以及其他人的等等。
我很困惑,每次迭代如何击中收集器会返回与此不同的结果。根据我的理解,垃圾是垃圾,所有这些应该改变的是在给定时间收集多少。
任何人都可以解释这种奇怪的行为吗?有没有人建议其他方法不频繁地调用收藏家?我的堆空间最大化了。
答案 0 :(得分:1)
您不应该依赖System.gc(),因为VM可以忽略它。如果你得到OutOfMemory,这意味着VM已经尝试运行GC。你可以尝试增加堆大小,改变堆中代的大小(比如你的大多数对象最后都是老代,然后你不需要太多的内存供年轻代使用),检查你的代码以确保你没有持有任何引用到你不需要的资源。
答案 1 :(得分:1)
在一般意义上调用System.gc()
是浪费时间,它不保证在任何时候都做任何事情,它最多只是建议并且在大多数情况下被忽略。在OutOfMemoryException
之后调用它更加无用,因为JVM在抛出异常之前已经尝试回收内存。
如果使用无法控制的第三方代码,唯一可以做的就是将命令行中的JVM堆分配增加到特定计算机可以处理的最多值。
Get started with java JVM memory (heap, stack, -xss -xms -xmx -xmn...)
答案 2 :(得分:1)
以下是我的建议:
-Xmx
执行此操作。参数。调用System.gc()
对您没有任何好处,因为它不能保证将调用GC。实际上,这是糟糕代码的明确标志。如果您依赖System.gc()
来运行代码,那么您的代码可能会被破坏。在这种情况下,您似乎是出于性能而依赖它,这表明您的代码肯定已损坏。
您永远无法确定JVM是否会尊重您的请求,您也无法告诉它如何执行垃圾收集。 JVM可能决定完全忽略您的请求(即,它不是保证)。 System.gc()
是否会做到它应该做的事情,是非常不确定的。由于不保证其行为,最好不要完全使用它。
最后,您可以使用System.gc()
选项禁用对-XX:DisableExplicitGC
的显式通话,这意味着再次无法保证您的System.gc()
通话将运行,因为它可能正在已配置为忽略该显式调用的JVM上运行。
答案 3 :(得分:1)
默认情况下,mstor会缓存从ehcache缓存中的文件夹中检索到的邮件,以便更快地访问。但是,可以禁用此缓存,我建议您为大文件夹禁用它。
您可以通过在类路径的根目录中创建名为“mstor.properties”的文本文件来禁用缓存,其中包含以下内容:
mstor.cache.disabled=true
您还可以将此值设置为系统属性:
java -Dmstor.cache.disabled=true SomeProgram
答案 4 :(得分:0)
mstor库没有很好地处理消息的缓存。在做了一些研究后,我发现如果你调用Folder.close()
(收件箱是我上面的文件夹对象),mstor和javaxmail会释放因getMessage()
方法而缓存的所有邮件。
我使try / catch块看起来像这样:
try {
message = inbox.getMessage(i);
// moved all of my calls to message.getFrom(),
// message.getAllRecipients(), etc. inside this try/catch.
} catch (OutOfMemoryError e) {
if (firstTry) {
i--;
firstTry = false;
} else {
firstTry = true;
System.out.println("Message " + i + " skipped.");
}
inbox.close(false);
System.gc();
inbox.open(Folder.READ_ONLY);
continue;
}
firstTry = true;
每次命中catch语句时,手动清除缓存的邮件并重新打开文件夹需要40-50 ms。
通过每次迭代调用垃圾收集器,解析一个1.6千兆字节的文件需要57分钟。使用此逻辑,解析同一文件只需18分钟。
更新 - 降低mstor使用的内存量的另一个重要方面是缓存属性。有人已经提到将“mstor.cache.disabled”设置为true,这有帮助。今天我发现了另一个重要的属性,它大大减少了更大文件的OOM捕获量。
Properties props = new Properties();
props.setProperty("mstor.mbox.metadataStrategy", "none");
props.setProperty("mstor.cache.disabled", "true");
props.setProperty("mstor.mbox.cacheBuffers", "false"); // most important