2

通过反复试验,我了解到这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 是一种可能性,因为可能需要保存未知(可能非常大)数量的数据?序列化和持久化到磁盘是解决这个问题的方法吗?

4

2 回答 2

6

为什么在字符串分配时没有发生这种情况?

list.add(...)方法也可以是分配内存。如果列表是一个,LinkedList那么每次add调用都会创建一个新的列表节点。如果它是ArrayList,那么add可能会导致支持数组被重新分配。

更新- 我刚刚注意到你甚至没有创建新的字符串对象。你不断地将相同的文字字符串添加"a"到列表中,这保证了 OOME不会在字符串分配中发生!)

知道OOM是一种可能性,如何最好地保护自己...

尝试捕捉并从 OOME 中恢复是很诱人的,但这可能是有风险的。问题是您永远无法确定您的应用程序(例如,您的应用程序代码调用的某个库方法)实际上试图分配什么,以及是否Error发生在不方便的时间并且在部分或部分中留下了一些重要的数据结构不一致的状态。因此,您的应用程序可能不适合尝试恢复。

通常,当您获得 OOME 时,最安全的做法是让应用程序立即退出。不要尝试提交事务等。当您的应用程序的数据库连接套接字/管道/任何东西被操作系统关闭时,让数据库的自动回滚清理任何未提交的事务。

事实上,“不要试图恢复”的建议适用于所有Error例外情况。只是 OOME 是开发人员倾向于忽略建议的情况,因为他们认为自己更了解......


在“保护自己”方面,一般的解决方案是在非易失性存储上安全地保存一份重要状态的副本;例如,通过将其写入数据库、序列化为平面文件等。具体细节(例如哪种技术最好)将取决于数据、您的应用程序如何使用它以及如何使您的应用程序可重新启动。

问题/情况与处理可能的应用程序崩溃、操作系统重启、电源故障等问题没有本质上的不同。

于 2012-07-06T02:46:49.697 回答
1

我会说不要尝试处理它;无论如何可以可靠地完成什么?

这是一个错误而不是异常。开始疯狂地挥手,如果合适的话,用看门狗重新启动这个过程。

如果怀疑超出可用内存量:

  1. 改变流式传输的方法和/或更精简;使用更少的[瞬态]内存
  2. 需要更多虚拟内存(OSJVM)
  3. 使用存储支持的结构:例如数据库、基于磁盘的哈希等(这是“序列化和持久化到磁盘”的一种形式)
于 2012-07-06T02:46:18.373 回答