我必须序列化大约一百万个项目,当我运行我的代码时,我得到以下异常:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOfRange(Unknown Source) at java.lang.String.(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)
我该如何处理?
理想情况下,重构代码以减少内存使用.例如,也许您可以流输出输出而不是将整个内容保存在内存中.
或者,只需使用该-Xmx
选项为JVM提供更多内存.
我知道Java的官方答案是"哦不!出于回忆!我放弃了!".对于那些在不允许内存不足(例如,编写操作系统或为非受保护的操作系统编写应用程序)的环境中编程的人来说,这一切都令人沮丧.
投降的意愿是必要的 - 您无法控制Java内存分配的每个方面,因此您无法保证您的程序在低内存条件下成功.但这并不意味着你必须不战而降.
但是,在战斗之前,你可以寻找避免这种需要的方法.也许你可以避免Java序列化,而是定义你自己的数据格式,不需要大量的内存分配来创建.序列化分配了大量内存,因为它保留了之前看到的对象的记录,因此如果它们再次出现,它可以通过数字引用它们而不是再次输出它们(这可能导致无限循环).但这是因为它需要是通用的:根据您的数据结构,您可能能够定义一些文本/二进制/ XML /任何只能写入流的表示,而几乎不需要存储额外的状态.或者您可以安排所需的任何额外状态始终存储在对象中,而不是在序列化时创建.
如果你的应用程序执行一项使用大量内存的操作,但主要使用的内存少得多,特别是如果该操作是用户启动的,并且如果你找不到使用更少内存或提供更多内存的方法,那么它可能值得捕捉OutOfMemory.您可以通过向用户报告问题太大来恢复,并邀请他们将其修剪下来并再试一次.如果他们只花了一个小时来解决他们的问题,你就不会我只想摆脱计划并失去一切 - 你想让他们有机会为此做点什么.只要错误被捕获到堆栈中,超出的内存将在捕获错误时被取消引用,从而使VM至少有机会恢复.确保捕获常规事件处理代码下面的错误(在常规事件处理中捕获OutOfMemory会导致繁忙循环,因为您尝试向用户显示对话框,您仍然内存不足,并且您捕获另一个错误).仅在您已识别为memory-hog的操作周围捕获它,以便不会捕获来自内存耗尽以外的代码的OutOfMemoryErrors.
即使在非交互式应用程序中,放弃失败的操作也是有意义的,但是程序本身可以继续运行,处理更多数据.这就是Web服务器管理多个进程的原因,如果一个页面请求由于内存不足而失败,服务器本身就不会崩溃.正如我在顶部所说的,单进程Java应用程序无法做出任何此类保证,但它们至少可以比默认值更强大.
也就是说,您的特定示例(序列化)可能不适合这种方法.特别是,用户在被告知存在问题时可能想要做的第一件事是保存他们的工作:但如果序列化失败,则可能无法保存.这不是你想要的,所以你可能不得不做一些实验和/或计算,并在尝试序列化之前手动限制程序允许的数百万项(基于它运行的内存量).
这比尝试捕获错误并继续更强大,但不幸的是,很难找出确切的界限,所以你可能不得不谨慎一点.
如果在反序列化过程中发生错误,那么您就会更加坚定:如果您可以避免加载文件,则不应该在应用程序中出现致命错误.捕获错误更可能是合适的.
无论你做什么来处理缺乏资源(包括让错误取消应用程序),如果你关心后果,那么彻底测试它是非常重要的.困难在于你永远不知道代码中究竟会出现什么问题,因此通常需要测试大量的程序状态.
你不应该在代码中处理它.不应该捕获和处理OutOfMemory.而是使用更大的堆空间启动JVM
java -Xmx512M
应该做的伎俩.
有关详细信息,请参见此处
其他人已经介绍了如何为Java提供更多内存,但由于"句柄"可能意味着捕获,我将引用Sun对Error
s的说法:
An
Error
是一个子类Throwable
,表示合理的应用程序不应该试图捕获的严重问题.大多数此类错误都是异常情况.
(强调我的)
您得到一个OutOfMemoryError,因为您的程序需要的内存比JVM可用的内存多.在运行时没有什么可以帮助实现的.
正如krosenvold所指出的那样,你的应用程序可能对内存提出了合理的要求,但恰好JVM没有足够的启动(例如你的应用程序将有280MB的峰值内存占用,但JVM只能以256MB开始).在这种情况下,增加分配的大小将解决这个问题.
如果您觉得在启动时提供了足够的内存,那么您的应用程序可能会暂时使用过多内存,或者内存泄漏.在您发布的情况下,听起来您正在同时持有对内存中所有百万项的引用,即使您可能正在按顺序处理它们.
检查你的参考资料对于"完成"的项目是什么样的 - 你应该尽快推荐它们,以便它们可以被垃圾收集.例如,如果要向集合中添加一百万个项目然后迭代该集合,则需要足够的内存来存储所有这些对象实例.看看你是否可以一次取一个对象,序列化它然后丢弃引用.
如果您在解决此问题时遇到问题,发布伪代码段会有所帮助.