【问题标题】:Recover from java.lang.OutOfMemoryError从 java.lang.OutOfMemoryError 中恢复
【发布时间】:2016-08-29 14:06:31
【问题描述】:

所以今天在学习Java的时候,终于遇到了这个特别的错误。似乎这个错误非常普遍,试图从中恢复得到了不同的反应。一些人认为it is useful in certain scenarios 所以它是一个很好的“好消息”,some have used it in their projects,而其他人vehemently oppose the idea 发现这个错误,然后是很多 others are just as confused as me

编辑:哦顺便说一句,这是我遇到的错误。我希望抓住这些错误,下次将增量器的值减少 1/10,直到发现另一个错误
(依此类推......直到我找到上限)

由于我在 Java 中迈出了第一步,找不到关于这个主题的任何具体内容,我想就这个特定的例子寻求你的帮助:

public class SpeedDemoClass {
        static int iterations=0;
        static int incrementor=10000000;
    public static void main(String[] args) {
        while(incrementor>1){
            try{
              iterations=iterations+incrementor;
              iterator(iterations);
            }
            catch(Exception e){
                System.out.println("So this is the limiting error: "+e);
                iterations=iterations-incrementor;
                incrementor=incrementor/10;
                iterations=iterations+incrementor;
            }
        }
        System.out.println("The upper limit of iterations is: "+iterations);
    }

    public static void iterator(int a){
            long start_time= System.currentTimeMillis();
            StringBuilder sbuild= new StringBuilder("JAVA");
            for(int i=0;i<a;i++){
                sbuild.append("JAVA");
            }
            System.out.println("Performing "+a+" append operations;"
                    +"process completed in :"
                    +(System.currentTimeMillis()-start_time)+"ms");
    }
}

你试过编译吗?

当然,它不起作用

下面是我正在尝试做的简短描述!

我正在尝试执行以下操作:

  1. 初始化 incrementor=10000000 和 iterations=0 并将 incrementor=incrementor+iterations 的结果传递给 iterator()。
  2. 如果出现错误,程序应捕获它,然后将 iterations* 减少 **incrementor,然后将 incrementor 除以 10,然后再试一次李>
  3. 重复步骤 1 到 2,直到它开始工作。
  4. 目标是找到产生错误的 iterations 值,并在每次迭代后保持在该级别以下,直到找到 iterations 值的上限
    (即当 incrementor 变为 0)

通过我自己的手动测试,我发现它的价值是 92,274,686。这就是神奇的数字。我不知道为什么会这样,也不知道它是否只对我自己的电脑有价值。这将是 如果有人可以发布一个可以吐出这个的修改过的代码,那就太棒了 结果。

【问题讨论】:

  • 你有什么问题?
  • 感谢您发布如此有条理的问题。
  • 结构良好,是的,但最终不清楚实际问题是什么。
  • 我希望通过 incrementor(X) 递增可能的迭代次数(I) 的值,直到出现错误。然后,当我发现错误时,我会将 X 的值更改为其值的 1/10 并重复该过程,直到再次出现错误,然后再捕获它并再次执行相同的操作。这一直重复,直到我有足够的 1/10 X 次达到 1,然后在下一个错误时,我达到 X=0;表示迭代结束。我相信,如果我这样做,我将能够找到 92,274,686。我还不知道如何使用 Java。我希望得到帮助:)
  • 您可能会在这个相关问题中找到有用的信息:stackoverflow.com/questions/37335/…

标签: java out-of-memory


【解决方案1】:

您可以看到Exception,但OutOfMemoryErrorError。请注意,ErrorException 在 Java 中是两个不同的类!不过,两者都实现了Throwable

catch 子句限制要捕获的 Throwable 类的类型。由于Error 不是Exception,您的代码不会捕获OutOfMemoryError

【讨论】:

    【解决方案2】:

    如果您分配一些用完内存的额外对象、使用内存较少的 PC 等或智能分配内存,那么“幻数”将会改变。 尝试暴力估计内存消耗是个坏主意!

    相反,要了解 Java 内存分配的工作原理、字符串构建器的内存布局以及如何控制它。在这里,你应该学习方法ensureCapacityInternal

    查看您得到的错误 - 特别是类和行号。

    我敢打赌,如果你替换这一行:

    StringBuilder sbuild= new StringBuilder("JAVA");
    

    通过

    StringBuilder sbuild= new StringBuilder(500000000);
    

    在看到 OOM 错误之前,您会走得更远。捕捉这个错误并不是一个非常聪明的方法。有一些很好的变体可以观察内存消耗;但是你真的应该只有在你了解了 Java 的内存组织之后才能使用它们,这样你就不会得出错误的结论。

    您的 PC 可能有 8 GB 的 RAM?尝试使用java -Xmx7500m。您可能可以走 3 倍。因为默认情况下,Java 只使用 25% 的内存,只是为了让其他进程也使用一些内存。如果你想让 Java 使用你所有的内存,你必须告诉它这是需要的。

    【讨论】:

      【解决方案3】:

      兄弟要捕获 outofmemoryerror,只需将 catch 中的 Exception 替换为 OutOfMemoryError

      catch(OutOfMemoryError e)

      【讨论】:

      • 我正要发布这个答案。几分钟前,我在今天早些时候看到的SO Question 中偶然发现了这一点。 不知道为什么错过了哈哈
      【解决方案4】:

      内存不足通常意味着您无能为力,VM 已经崩溃。虽然您似乎可以做一些事情,例如释放内存,但 Java 会为您完成所有内存管理。你不能释放任何东西,你可以取消引用对象然后触发垃圾回收,但是在异常发生的那一刻为时已晚。几乎你能做的任何事情都需要内存分配,而你没有。

      一般来说,VM 引发的所有错误都无法恢复。通常是因为虚拟机本身不稳定,并且您无权访问虚拟机的内部来修复它。不仅如此,您的软件可以修复它的情况很少见。

      【讨论】:

      • JVM 抛出 OutOfMemoryError 不会使其不稳定。它只是表示 - 与任何其他异常一样 - try 块中的代码无法执行。此外,这并不意味着 VM 已崩溃。只有应用程序不(或不能)处理错误时才会崩溃。在这个问题中,从错误中恢复很容易,因为只要iterator 方法返回(正常或异常),内存就会被释放。
      【解决方案5】:

      我打算发布此解决方案,但 somebody beat me to it。今天早些时候,我似乎忽略了one of the alternative questions of this sort 中的a solution,我在this question 开头提到过。

      好吧,只是出于一般的礼貌,为了让未来的人们不会像我一样忽视一个直接的解决方案,我发布了一个简单的答案*。以下是我为实现这一目标所做的一些小改动。看来,有可能捕获 java.lang.OutOfMemoryError 虽然我还不知道我真正所做的事情的含义。

      * 请随时纠正我并阅读此答案的结尾 :)

      我会在以后更新这个答案,以反映我以后可能会发现的事情(比如差异等

      所以事不宜迟,下面是原始荣耀中的代码:

      public class SpeedDemoClass {
          static int iterations=0;
          static int incrementor=10000000;
          public static void main(String[] args) {
              while(incrementor>0){
                  try{
                      iterations=iterations+incrementor;
                      int a = iterations;
                      long start_time= System.currentTimeMillis();
                      StringBuilder sbuild= new StringBuilder("JAVA");
                      for(int i=0;i<a;i++){
                          sbuild.append("JAVA");
                      }
                      System.out.println("Performing "+a+" append operations;"
                          +"process completed in :"
                          +(System.currentTimeMillis()-start_time)+"ms");
                  }
                  catch(OutOfMemoryError e){
                      System.out.println("OutOfMemory bound reached beyond this point with error: "+e
                      +"\nReverting back, and Changing the value of incrementor by 1/10th...");
                      iterations=iterations-incrementor;
                      incrementor=incrementor/10;
                      iterations=iterations+incrementor;
                  }
              }
              System.out.println("The upper limit of iterations is: "+iterations);
          }
      }
      

      更新日志:
      如果还不明显,我对the original code 做了一些微小的更改。
      (1) 我放弃了iterator() 方法,因为我现在意识到它让人们更难理解问题的意图。
      (2) 我将 catch(Exception e) 更改为 catch(OutOfMemoryError e)Turns out there is a native solution available,我之前忽略了。

      输出:

      这对我来说似乎很有效,现在我找到了神奇的数字。我欢迎所有可以表明此答案错误的 cmets,为什么? :)

      【讨论】:

        猜你喜欢
        • 2013-06-05
        • 2010-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-18
        • 1970-01-01
        相关资源
        最近更新 更多