如何处理 Java 中的 OutOfMemoryError?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/511013/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to handle OutOfMemoryError in Java?
提问by
I have to serialize around a million items and I get the following exception when I run my code:
我必须序列化大约一百万个项目,并且在运行代码时出现以下异常:
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)
How do I handle this?
我该如何处理?
回答by krosenvold
Start java with a larger value for option -Xmx, for instance -Xmx512m
使用更大的选项 -Xmx 启动 java,例如 -Xmx512m
回答by Kieron
Ideally, restructure your code to use less memory. For example, perhaps you could stream the output instead of holding the whole thing in memory.
理想情况下,重构您的代码以使用更少的内存。例如,也许您可以流式传输输出而不是将整个内容保存在内存中。
Alternatively, just give the JVM more memory with the -Xmx
option.
或者,只需使用该-Xmx
选项为 JVM 提供更多内存。
回答by Mo.
回答by Mnementh
You can increase the size of the memory java uses with the -Xmx-option, for instance:
您可以使用 -Xmx 选项增加 java 使用的内存大小,例如:
java -Xmx512M -jar myapp.jar
Better is to reduce the memory-footprint of your app. You serialize millions of items? Do you need to keep all of them in memory? Or can you release some of them after using them? Try to reduce the used objects.
更好的是减少应用程序的内存占用。您序列化了数百万个项目?你需要把它们都保存在内存中吗?或者你可以在使用它们后释放它们中的一些吗?尽量减少使用的对象。
回答by Andrzej Doyle
You get an OutOfMemoryError because your program requires more memory than the JVM has available. There is nothing you can specifically do at runtime to help this.
您收到 OutOfMemoryError 是因为您的程序需要比 JVM 可用的更多内存。您无法在运行时专门做任何事情来帮助解决此问题。
As noted by krosenvold, your application may be making sensible demands for memory but it just so happens that the JVM is not being started with enough (e.g. your app will have a 280MB peak memory footprint but the JVM only starts with 256MB). In this case, increasing the size allocated will solve this.
正如 krosenvold 所指出的,您的应用程序可能对内存提出了合理的需求,但碰巧 JVM 启动时没有足够的内存(例如,您的应用程序将有 280MB 的峰值内存占用,但 JVM 仅以 256MB 启动)。在这种情况下,增加分配的大小将解决这个问题。
If you feel that you are supplying adequate memory at start up, then it is possible that your application is either using too much memory transiently, or has a memory leak. In the situation you have posted, it sounds like you are holding references to all of the million items in memory at once, even though potentially you are dealing with them sequentially.
如果您觉得在启动时提供了足够的内存,那么您的应用程序可能暂时使用了过多的内存,或者存在内存泄漏。在您发布的情况下,听起来您同时持有对内存中所有百万项的引用,即使您可能正在按顺序处理它们。
Check what your references are like for items that are "done" - you should deference these as soon as possible so that they can be garbage collected. If you're adding a million items to a collection and then iterating over that collection, for example, you'll need enough memory to store all of those object instances. See if you can instead take one object at a time, serialise it and then discard the reference.
检查您对“完成”项目的引用情况 - 您应该尽快尊重这些内容,以便它们可以被垃圾收集。例如,如果您将一百万个项目添加到一个集合中,然后对该集合进行迭代,那么您将需要足够的内存来存储所有这些对象实例。看看您是否可以一次取一个对象,将其序列化,然后丢弃引用。
If you're having trouble working this out, posting a pseudo-code snippet would help.
如果您在解决此问题时遇到问题,发布伪代码片段会有所帮助。
回答by Martin OConnor
Use the transient keyword to mark fields in the serialized classes which can be generated from existing data. Implement writeObject and readObject to help with reconstructing transient data.
使用transient 关键字标记可以从现有数据生成的序列化类中的字段。实现 writeObject 和 readObject 以帮助重建瞬态数据。
回答by David Santamaria
In addition to some of the tips that have been give to you, as review the memory lacks and also start the JVM with more memory (-Xmx512M). Looks like you have a OutOfMemoryError cause your TopicParseris reading a line that probably is pretty big (and here is what you should avoid), you can use the FileReader(or, if the encoding is an issue, an InputStreamReaderwrapping a FileInputStream). Use its read(char[]) method with a reasonablysized char[] array as a buffer.
除了已经提供给您的一些提示之外,请查看内存不足并使用更多内存 (-Xmx512M) 启动 JVM。看起来你有一个 OutOfMemoryError ,因为你的TopicParser正在读取一条可能很大的行(这是你应该避免的),你可以使用FileReader(或者,如果编码是一个问题,一个InputStreamReader包装FileInputStream)。使用它的 read(char[]) 方法和一个合理大小的 char[] 数组作为缓冲区。
Also finally to investigate a little why is the OutOfMemoryError you can use -XX:+HeapDumpOnOutOfMemoryError Flag in the JVM to get a dump heap information to disk.
最后还要调查一下为什么会出现 OutOfMemoryError 你可以在 JVM 中使用 -XX:+HeapDumpOnOutOfMemoryError Flag 来获取转储堆信息到磁盘。
Good luck!
祝你好运!
回答by Anonymous
There's no real way of handling it nicely. Once it happens you are in the unknown territory. You can tell by the name - OutOfMemoryError. And it is described as:
没有真正的方法可以很好地处理它。一旦发生,你就处于未知领域。您可以通过名称来判断 - OutOfMemory Error。它被描述为:
Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector
当 Java 虚拟机由于内存不足而无法分配对象并且垃圾收集器无法提供更多内存时抛出
Usually OutOfMemoryError indicates that there is something seriously wrong with the system/approach (and it's hard to point a particular operation that triggered it).
通常 OutOfMemoryError 表示系统/方法存在严重错误(并且很难指出触发它的特定操作)。
Quite often it has to do with ordinary running out of heapspace. Using the -verbosegc and mentioned earlier -XX:+HeapDumpOnOutOfMemoryError should help.
通常它与普通的堆空间耗尽有关。使用 -verbosegc 和前面提到的 -XX:+HeapDumpOnOutOfMemoryError 应该会有所帮助。
You can find a nice and concise summary of the problem at javaperformancetuning
您可以在javaperformancetuning 中找到有关该问题的简洁明了的摘要
回答by Steve Jessop
I know that the official Java answer is "Oh noes! Out of memories! I give in!". This is all rather frustrating for anyone who has programmed in environments where running out of memory is not allowed to be a fatal error (for example, writing an OS, or writing apps for non-protected OSes).
我知道官方的 Java 回答是“哦,不!出于记忆!我屈服了!”。对于在不允许将内存不足视为致命错误的环境(例如,编写操作系统,或为不受保护的操作系统编写应用程序)的环境中进行编程的任何人来说,这都是相当令人沮丧的。
The willingness to surrender is necessary - you can't control every aspect of Java memory allocation, so you can't guarantee that your program will succeed in low-memory conditions. But that doesn't mean you must go down without a fight.
放弃的意愿是必要的——您无法控制 Java 内存分配的每个方面,因此您无法保证您的程序在低内存情况下会成功。但这并不意味着你必须不战而降。
Before fighting, though, you could look for ways to avoid the need. Perhaps you can avoid Java serialization, and instead define your own data format which does not require significant memory allocation to create. Serialization allocates a lot of memory because it keeps a record of objects it has seen before, so that if they occur again it can reference them by number instead of outputting them again (which could lead to an infinite loop). But that's because it needs to be general-purpose: depending on your data structure, you might be able to define some text/binary/XML/whatever representation which can just be written to a stream with very little need to store extra state. Or you might be able to arrange that any extra state you need is stored in the objects all along, not created at serialization time.
不过,在战斗之前,您可以寻找避免这种需要的方法。也许您可以避免 Java 序列化,而是定义您自己的数据格式,该格式不需要大量内存分配即可创建。序列化会分配大量内存,因为它保留了之前见过的对象的记录,因此如果它们再次出现,它可以通过编号引用它们而不是再次输出它们(这可能导致无限循环)。但那是因为它需要是通用的:根据您的数据结构,您可能能够定义一些文本/二进制/XML/任何可以写入流的表示,几乎不需要存储额外的状态。或者您可以安排将您需要的任何额外状态一直存储在对象中,而不是在序列化时创建。
If your application does one operation which uses a lot of memory, but mostly uses much less, and especially if that operation is user-initiated, and if you can't find a way to use less memory or make more memory available, then it might be worth catching OutOfMemory. You could recover by reporting to the user that the problem is too big, and inviting them to trim it down and try again. If they've just spend an hour setting up their problem, you do notwant to just bail out of the program and lose everything - you want to give them a chance to do something about it. As long as the Error is caught way up the stack, the excess memory will be unreferenced by the time the Error is caught, giving the VM at least a chance to recover. Make sure you catch the error below your regular event-handling code (catching OutOfMemory in regular event handling can result in busy loops, because you try to display a dialog to the user, you're still out of memory, and you catch another Error). Catch it only around the operation which you've identified as the memory-hog, so that OutOfMemoryErrors you can't handle, that come from code other than the memory hog, are not caught.
如果您的应用程序执行一个使用大量内存的操作,但大部分使用的内存要少得多,特别是如果该操作是用户启动的,并且如果您找不到使用更少内存或提供更多内存的方法,那么它可能值得抓住 OutOfMemory。您可以通过向用户报告问题太大来恢复,并邀请他们缩小并重试。如果他们只花了一个小时来解决他们的问题,你就不会只想退出该计划并失去一切-您想给他们一个机会来做些什么。只要错误在堆栈中被捕获,在错误被捕获时多余的内存将不被引用,至少给 VM 一个恢复的机会。确保在常规事件处理代码下方捕获错误(在常规事件处理中捕获 OutOfMemory 可能导致繁忙循环,因为您尝试向用户显示对话框,但仍然没有内存,并且您捕获另一个错误)。仅在您确定为内存占用的操作周围捕获它,以便您无法处理的 OutOfMemoryErrors 来自内存占用以外的代码,不会被捕获。
Even in a non-interactive app, it might make sense to abandon the failed operation, but for the program itself to carry on running, processing further data. This is why web servers manage multiple processes such that if one page request fails for lack of memory, the server itself doesn't fall over. As I said at the top, single-process Java apps can't make any such guarantees, but they can at least be made a bit more robust than the default.
即使在非交互式应用程序中,放弃失败的操作也可能是有意义的,但程序本身可以继续运行,处理更多数据。这就是 Web 服务器管理多个进程的原因,这样如果一个页面请求因内存不足而失败,服务器本身不会崩溃。正如我在上面所说的,单进程 Java 应用程序不能做出任何这样的保证,但它们至少可以比默认的更健壮一些。
That said, your particular example (serialization) may not be a good candidate for this approach. In particular, the first thing the user might want to do on being told there's a problem is save their work: but if it's serialization which is failing, it may be impossible to save. That's not what you want, so you might have to do some experiments and/or calculations, and manually restrict how many million items your program permits (based on how much memory it is running with), beforethe point where it tries to serialize.
也就是说,您的特定示例(序列化)可能不适合这种方法。特别是,当用户被告知有问题时,他们可能想做的第一件事是保存他们的工作:但如果序列化失败,则可能无法保存。这不是您想要的,因此您可能需要做一些实验和/或计算,并在尝试序列化之前手动限制程序允许的数百万个项目(基于它运行的内存量)。
This is more robust than trying to catch the Error and continue, but unfortunately it's difficult to work out the exact bound, so you would probably have to err on the side of caution.
这比试图捕捉错误并继续更健壮,但不幸的是,很难计算出确切的界限,因此您可能不得不谨慎行事。
If the error is occurring during deserialization then you're on much firmer ground: failing to loada file should not be a fatal error in an application if you can possibly avoid it. Catching the Error is more likely to be appropriate.
如果错误是在反序列化期间发生的,那么您的理由要牢固得多:如果您可以避免,则无法加载文件不应该是应用程序中的致命错误。捕获错误更有可能是合适的。
Whatever you do to handle lack of resources (including letting the Error take down the app), if you care about the consequences then it's really important to test it thoroughly. The difficulty is that you never know exactly what point in your code the problem will occur, so there is usually a very large number of program states which need to be tested.
无论你做什么来处理资源不足(包括让错误关闭应用程序),如果你关心后果,那么彻底测试它真的很重要。困难在于您永远不知道问题会在代码中的哪一点发生,因此通常有大量的程序状态需要测试。
回答by Brendan Cashman
After you follow the suggestion of increasing heap space (via -Xmx) but sure to use either JConsoleor JVisualVMto profile your applications memory usage. Make sure that memory usage does not continuously grow. If so you'll still get the OutOfMemoryException, it'll just take longer.
在您遵循增加堆空间的建议(通过 -Xmx)但确保使用JConsole或JVisualVM来分析您的应用程序内存使用情况之后。确保内存使用不会持续增长。如果是这样,您仍然会收到 OutOfMemoryException,只是需要更长的时间。