JVM在内存不足错误期间的行为? List s = new ArrayList ();

try { for(;;) { s.add("Pradeep"); } } finally { System.out.println("In Finally"); } 

在try块中,jvm耗尽了内存,那么jvm如何在没有内存的情况下最终阻塞?

输出:

 In Finally Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 

这是更简单的代码,更好地展示了发生的事情:

  try { int[] a = new int[Integer.MAX_VALUE]; } catch( Exception e ) { e.printStackTrace(); } 

数组的分配失败,但这并不意味着Java不再有空闲内存。 如果将项添加到列表中,则列表会以跳转的forms增长。 同时,该列表将需要VM的一半以上的内存(默认情况下约为64MB)。 下一个添加将尝试分配一个太大的数组。

但这意味着VM仍有大约30MB的未使用堆剩余用于其他任务。

如果您想让VM遇到麻烦,请使用LinkedList因为它会线性增长。 当最后一次分配失败时,只有很少的内存来处理exception:

  LinkedList list = new LinkedList(); try { for(;;) { list.add(0); } } catch( Exception e ) { e.printStackTrace(); } 

该程序需要更长时间才能终止,但它仍然会以错误终止。 也许Java会将部分堆放在一边以进行error handling或error handling不需要分配内存(分配在代码执行之前发生)。

据推测, System.out.println调用所需的内存少于s.add("Pradeep")调用。

例如,如果sArrayList ,则s.add调用可能会导致列表尝试将其容量加倍。 这可能是一个非常需要内存的操作,因此即使它不能执行这样相对昂贵的任务,JVM也可以继续执行并不奇怪。

在try块中,jvm耗尽了内存,那么jvm如何在没有内存的情况下最终阻塞?

JVM“耗尽内存”并在尝试分配对象或数组失败时抛出OOME,因为没有足够的空间可用于该对象 。 这并不意味着一切都必须立即停止:

  • JVM可以愉快地继续做一些不需要创建任何新对象的事情。 我很确定在这种情况下会发生这种情况。 (字符串文字已经存在, println方法的实现很可能是将字符复制到先前分配的Buffer中。)

  • JVM可能会分配小于触发OOME的对象。

  • OOME的传播可能导致包含对象引用的变量超出范围,并且它们引用的对象可能变得无法访问。 然后,随后的new触发器可以触发回收所述对象的GC,为新对象腾出空间。


注意:JLS没有指定在加载类时必须创建表示该文字的String对象。 但是,它肯定会说它可能是在类加载时创建的……这就是在任何体面的JVM中发生的事情。


另一个答案说:

也许Java会将部分堆放在一边以进行error handling或error handling不需要分配内存(分配在代码执行之前发生)。

我认为这是对的。 但是,我认为这个特殊的堆区域仅在实例化OOMEexception时使用,并填充堆栈跟踪信息。 一旦发生这种情况,JVM就会回到使用常规堆。 (很容易得到一些证据。只需在finally块中重试add调用。如果成功,那就certificate某些事情已经使更多内存可用于一般用途。)

JVM实际上没有内存,无法继续。 此错误表示无法分配内存,因此没有。 这可能意味着它非常低。 但在这里失败的是调整集合内部数组的大小。 还有很多内存可以让大型数据翻倍。 所以最终可以继续进行。

当堆空间超过-Xmx标志设置的空间时,将引发错误,并且无法正常继续。 错误传播,但它不会立即导致JVM关闭(在这种情况下退出系统,OOM错误中没有任何意义,因为它永远不会被抛出)。

由于JVM尚未退出,因此根据语言规范,它将尝试执行finally块。

最后几乎总是执行。 抛出exception时,JVM尽可能多地收集内存,读取代码可能意味着它收集了整个集合。

当到达finally时, 它只需要在字符串池中创建一个新的字符串“In finally”,不需要额外的内存,并且它没有问题,因为它之前释放了空间。

尝试在finally上打印s.size() ,你会看到它是如何做不到的。 (编辑:如果在catch中,最后或在try块之后,有一行代码使用s集合,垃圾收集器在抛出OOME时无法收集它。这就是为什么堆内存几乎是full,所以任何新的对象分配都可能再次抛出另一个OOME。如果没有看到完整的代码,很难预测。)