我必须序列化大约一百万个项目,并且在运行代码时出现以下异常:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOfRange(Unknown Source)
at java.lang.String.<init>(Unknown Source)
at java.io.BufferedReader.readLine(Unknown Source)
at java.io.BufferedReader.readLine(Unknown Source)
at org.girs.TopicParser.dump(TopicParser.java:23)
at org.girs.TopicParser.main(TopicParser.java:59)
我该如何处理?
答案 0 :(得分:28)
理想情况下,重构代码以减少内存使用。例如,也许您可以流式传输输出而不是将整个内容保存在内存中。
或者,只需使用-Xmx
选项为JVM提供更多内存。
答案 1 :(得分:27)
我知道Java的正式答案是“哦不!出于回忆!我放弃了!”。对于那些在不允许内存不足(例如,编写操作系统或为不受保护的操作系统编写应用程序)的环境中进行编程的人来说,这一点都令人沮丧。
投降的意愿是必要的 - 您无法控制Java内存分配的每个方面,因此您无法保证您的程序在低内存条件下成功。但这并不意味着你必须不战而降。
但是,在战斗之前,你可以寻找避免这种需要的方法。也许你可以避免Java序列化,而是定义你自己的数据格式,不需要大量的内存分配来创建。序列化分配了大量内存,因为它保留了之前看到的对象的记录,因此如果它们再次出现,它可以通过数字引用它们而不是再次输出它们(这可能导致无限循环)。但这是因为它需要是通用的:根据您的数据结构,您可能能够定义一些文本/二进制/ XML /任何只能写入流的表示,而几乎不需要存储额外的状态。或者您可以安排所需的任何额外状态始终存储在对象中,而不是在序列化时创建。如果你的应用程序执行一项使用大量内存的操作,但主要使用的内存少得多,特别是如果该操作是用户启动的,并且你找不到使用更少内存或提供更多内存的方法那么它可能值得捕捉OutOfMemory。您可以通过向用户报告问题太大来恢复,并邀请他们将其修剪下来并再试一次。如果他们只花了一个小时来设置他们的问题,那么不想要摆脱计划而失去一切 - 你想让他们有机会对此做些什么。只要错误被捕获到堆栈中,超出的内存将在捕获错误时被取消引用,从而使VM至少有机会恢复。确保捕获常规事件处理代码下面的错误(在常规事件处理中捕获OutOfMemory会导致繁忙循环,因为您尝试向用户显示对话框,您仍然内存不足,并且您捕获另一个错误)。只能在你已经识别为memory-hog的操作周围捕获它,这样你就无法处理来自内存以外的代码的OutOfMemoryErrors,而不会被捕获。
即使在非交互式应用程序中,放弃失败的操作也是有意义的,但程序本身可以继续运行,处理更多数据。这就是Web服务器管理多个进程的原因,如果一个页面请求由于内存不足而失败,服务器本身就不会崩溃。正如我在顶部所说的那样,单进程Java应用程序无法做出任何此类保证,但它们至少可以比默认值更强大。
那就是说,你的特定例子(序列化)可能不适合这种方法。特别是,用户在被告知存在问题时可能想要做的第一件事是保存他们的工作:但如果序列化失败,则可能无法保存。这不是你想要的,所以你可能不得不做一些实验和/或计算,并手动限制程序允许的数百万项(基于它运行的内存量),之前它试图序列化的地方。
这比尝试捕获错误并继续更强大,但不幸的是,很难找出确切的界限,所以你可能不得不小心谨慎。
如果在反序列化过程中发生错误,那么您就会更加坚定:如果您可以避免错误,则无法加载文件不应该是应用程序中的致命错误。捕获错误更可能是合适的。
无论你做什么来处理缺乏资源(包括让错误取消应用程序),如果你关心后果,那么彻底测试它是非常重要的。困难在于你永远不知道代码中究竟会出现什么问题,因此通常会有大量的程序状态需要测试。
答案 2 :(得分:12)
答案 3 :(得分:8)
其他人已经介绍过如何为Java提供更多内存,但由于“句柄”可能意味着捕获,我将引用Sun对Error
s所说的内容:
Error
是Throwable
的子类 这表明存在严重的问题 合理的应用不应该尝试 赶上。大多数此类错误都是 异常情况。
(强调我的)
答案 4 :(得分:7)
你得到一个OutOfMemoryError,因为你的程序需要的内存比JVM可用的内存多。在运行时没有什么可以帮助实现的。
正如krosenvold所说,你的应用程序可能对内存提出了合理的要求,但恰好JVM没有足够的启动(例如你的应用程序将有280MB的峰值内存占用,但JVM只能以256MB开始) 。在这种情况下,增加分配的大小将解决这个问题。
如果您觉得在启动时提供了足够的内存,那么您的应用程序可能会暂时使用过多内存,或者内存泄漏。在您发布的情况下,听起来您正在同时持有对内存中所有百万项的引用,即使您可能正在按顺序处理它们。
检查“完成”项目的引用是什么样的 - 您应该尽快推荐它们,以便它们可以被垃圾收集。例如,如果要向集合中添加一百万个项目然后迭代该集合,则需要足够的内存来存储所有这些对象实例。看看你是否可以一次取一个对象,序列化然后丢弃参考。
如果您在解决此问题时遇到问题,发布伪代码段会有所帮助。
答案 5 :(得分:3)
除了提供给你的一些提示之外,还有记忆中缺少的一些提示 还可以使用更多内存(-Xmx512M)启动JVM。 看起来你有一个OutOfMemoryError导致你的 TopicParser 读取的行可能很大(这是你应该避免的),你可以使用 FileReader (或者,如果编码是一个问题, InputStreamReader 包装 FileInputStream )。使用read(char [])方法和合理大小的char []数组作为缓冲区。
最后还要研究一下为什么可以使用OutOfMemoryError -XX:+ HeapDumpOnOutOfMemoryError 在JVM中标记以获取转储堆信息到磁盘。
祝你好运!答案 6 :(得分:2)
使用transient关键字标记序列化类中可以从现有数据生成的字段。 实现writeObject和readObject以帮助重建瞬态数据。
答案 7 :(得分:2)
有趣 - 你在readline上的内存不足。猜测一下,你正在阅读没有换行符的大文件。
而不是使用readline将文件中的东西作为一个单独的大字符串来写出来,编写能够更好地理解输入的内容,并以块的形式处理它。
如果您只是必须将整个文件放在一个大的长字符串中......那么,在编码时要更好。一般来说,尝试通过将所有数据填充到一个字节(或其他)的单个数组来处理mutimegabyte数据是一种很好的方法。
去看看CharacterSequence。
答案 8 :(得分:1)
遵循增加堆空间的建议(通过-Xmx),但确保使用JConsole或JVisualVM来分析应用程序的内存使用情况。确保内存使用量不会持续增长。如果是这样,你仍然会得到OutOfMemoryException,它只需要更长的时间。
答案 9 :(得分:0)
使用选项-Xmx的较大值启动java,例如-Xmx512m
答案 10 :(得分:0)
您可以使用-Xmx-option增加java使用的内存大小,例如:
java -Xmx512M -jar myapp.jar
更好的是减少应用的内存占用量。你序列化了数百万件物品?你需要把所有这些都留在记忆中吗?或者你可以在使用后释放其中一些吗?尝试减少使用过的物体。
答案 11 :(得分:0)
没有真正妥善处理它的方法。一旦发生,你就处于未知领域。您可以通过名称 - OutOfMemory 错误来判断。它被描述为:
什么时候抛出 Java虚拟机无法分配对象,因为它不在 内存,垃圾无法提供更多内存 集电极
通常OutOfMemoryError表示系统/方法存在严重错误(并且很难指出触发它的特定操作)。
通常它与普通的堆空间用完有关。使用-verbosegc并在前面提到-XX:+ HeapDumpOnOutOfMemoryError应该有所帮助。
找到问题的简洁摘要答案 12 :(得分:0)
在采取任何危险,耗时或战略性行动之前,您应该准确确定程序中正在耗尽大量内存的内容。你可能认为你知道答案,但在你面前有证据之前,你就不会。存储器有可能被你没想到的东西使用。
使用分析器。哪一个there are plenty of them并不重要。首先找出每个对象消耗了多少内存。其次,通过迭代序列化程序,比较内存快照并查看创建的对象或数据。
答案很可能是流输出而不是在内存中构建输出。但首先得到证据。
答案 13 :(得分:0)
我发现了一个替代方案,尊重所有其他观点,我们不应该试图从异常中捕获内存,这是我最近学到的。
catch (Throwable ex){
if (!(ex instanceof ThreadDeath))
{
ex.printStackTrace(System.err);
}}
供您参考:OutOfMemoryError 任何反馈都是受欢迎的。
Avishek Arang