通过反复试验,我了解到11_451_104
是一个神奇的数字,这会导致我的机器抛出OOM错误。
使用11_451_103
,我正在打包尽可能多的数据。
private static void init() {
int i = 0;
try {
while (++i < 11451104) {
list.add("a");
}
} catch (OutOfMemoryError e) {
System.out.println("oh no, not again :("); // <-- Not getting here
}
}
如果在下一行我做
String x = "some new string";
我希望发生异常,因为堆不能为1个字符串分配空间。但事实并非如此。
如果我尝试将此新字符串添加到列表中,
String x = "some new string"; // <-- expect OOM Error to happen here
list.add(x);
程序确实在OOM中止。 为什么在字符串分配时不会发生这种情况?
如何最好地保护自己知道OOM是可能的,因为可能需要保存未知(可能非常大)的数据量?对磁盘进行序列化和持久化是一种处理此问题的方法吗?
答案 0 :(得分:6)
为什么在字符串分配时不会发生这种情况?
list.add(...)
方法也可能是分配内存。如果列表是LinkedList
,则每个add
调用都会创建一个新的列表节点。如果是ArrayList
,则add
可能会导致重新分配支持数组。
( UPDATE - 我刚注意到你甚至没有创建新的字符串对象。你不断向列表中添加相同的文字字符串"a"
,并且保证 OOME 不会在字符串分配中出现!)
如何最好地保护自己知道OOM是可能的......
尝试从OOME中捕获并恢复是很诱人的,但这可能有风险。问题是你永远不知道你的应用程序(例如你的应用程序代码调用的某些库方法)实际上是在尝试分配什么,以及Error
是否在不方便的时候发生并留下了一些重要数据处于部分或不一致状态的结构。因此,您的申请可能不适合尝试恢复。
通常,获得OOME时最安全的做法是立即退出应用程序。不要尝试提交事务等。当应用程序的数据库连接套接字/管道/任何内容被操作系统关闭时,让数据库的自动回滚清理所有未提交的事务。
事实上,“不要尝试恢复”的建议适用于所有Error
例外情况。只是OOME的情况是开发人员倾向于忽视这些建议,因为他们认为他们知道的更好......
在“保护自己”方面,一般的解决方案是在非易失性存储上安全地保存重要状态的副本;例如通过将其写入数据库,序列化为平面文件等。具体内容(例如哪种技术最佳)将取决于数据,应用程序如何使用它,以及如何使应用程序重新启动。
问题/情况与处理可能的应用程序崩溃,操作系统重启,电源故障等问题没有本质上的区别。
答案 1 :(得分:1)
我会说不要尝试处理它;什么可以可靠地完成?
这是一个错误而不是例外。如果合适的话,开始疯狂地挥手并用看门狗重新开始这个过程。
如果怀疑超出可用内存量: