通过反复试验,我得知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是一种可能?磁盘的序列化和持久化是否可以解决此问题?
最佳答案
为什么在分配字符串时没有发生这种情况?list.add(...)
方法也可能正在分配内存。如果列表是LinkedList
,则每个add
调用都会创建一个新的列表节点。如果它是ArrayList
,则add
可能导致重新分配后备阵列。
(更新-我只是注意到您甚至没有创建新的字符串对象。您正在不断向列表中添加相同的文字字符串"a"
,这保证了OOME在字符串分配中不会发生!)
如何最好地保护自己,知道OOM是一种可能...
尝试从OOME中捕获和恢复是很诱人的,但是这样做可能会带来风险。问题是您永远无法确定自己的应用程序(例如,由应用程序代码调用的某些库方法)实际上是在尝试分配什么,以及Error
是否在不方便的时间发生并在其中留下了一些重要的数据结构部分或不一致的状态。因此,您的应用程序可能处于无法恢复的合适状态。
通常,获得OOME时最安全的做法是使应用程序立即退出。不要尝试提交事务,等等。当应用程序的数据库连接套接字/管道/任何东西被操作系统关闭时,让数据库的自动回滚清除所有未提交的事务。
实际上,“请勿尝试恢复”建议适用于所有Error
异常。仅仅是OOME的情况下,开发人员倾向于忽略建议,因为他们认为他们更了解...
在“保护自己”方面,一般的解决方案是将重要状态的副本安全地保存在非易失性存储中;例如通过将其写入数据库,序列化为平面文件等。具体细节(例如,哪种技术最好)将取决于数据,应用程序如何使用它以及如何使应用程序可重启。
问题/情况在本质上与处理可能的应用程序崩溃,操作系统重新启动,电源故障等问题没有任何不同。
关于java - 关于OutOfMemoryError及其处理方式,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11355138/