Java OutOfMemoryError奇怪的行为 [英] Java OutOfMemoryError strange behaviour

查看:95
本文介绍了Java OutOfMemoryError奇怪的行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们的最大内存为256M,为什么这段代码有效:

Assuming we have a max memory of 256M, why does this code work:

public static void main(String... args) {
  for (int i = 0; i < 2; i++)
  {
      byte[] a1 = new byte[150000000];
  }
  byte[] a2 = new byte[150000000];
}

但是这个扔了一个OOME?

but this one throw an OOME?

public static void main(String... args) {
  //for (int i = 0; i < 2; i++)
  {
      byte[] a1 = new byte[150000000];
  }
  byte[] a2 = new byte[150000000];
}


推荐答案

为了保持透视,考虑使用 -Xmx64m 运行此代码:

To keep things in perspective, consider running this code with -Xmx64m:

static long sum;
public static void main(String[] args) {
  System.out.println("Warming up...");
  for (int i = 0; i < 100_000; i++) test(1);
  System.out.println("Main call");
  test(5_500_000);
  System.out.println("Sum: " + sum);
}

static void test(int size) {
//  for (int i = 0; i < 1; i++)
  {
    long[] a2 = new long[size];
    sum += a2.length;
  }
  long[] a1 = new long[size];
  sum += a1.length;
}

根据您是做热身还是跳过它,它会不会被打击吹。这是因为JITted代码正确 null 输出var,而解释后的代码则没有。这两种行为在Java语言规范下都是可以接受的,这意味着你受JVM的支配。

Depending on whether you do the warmup or skip it, it will blow or not blow. This is because the JITted code properly nulls out the var, whereas the interpreted code doesn't. Both behaviors are acceptable under the Java Language Specification, which means that you are at the mercy of the JVM with this.


用<$测试c $ c> OS X上的Java HotSpot(TM)64位服务器VM(版本23.3-b01,混合模式)。

查看的字节码,用于循环(简单代码,没有总和变量):

Look at the bytecode with the for loop (simple code, without the sum variable):

static void test(int);
  Code:
   0: iconst_0
   1: istore_1
   2: goto  12
   5: iload_0
   6: newarray long
   8: astore_2
   9: iinc  1, 1
   12:  iload_1
   13:  iconst_1
   14:  if_icmplt 5
   17:  iload_0
   18:  newarray long
   20:  astore_1
   21:  return

且没有:

static void test(int);
  Code:
   0: iload_0
   1: newarray long
   3: astore_1
   4: iload_0
   5: newarray long
   7: astore_1
   8: return

没有明确的 null 在任何一种情况下输出,但请注意,在 no-for 示例中,实际上重用了相同的内存位置,与 for 示例相反。如果有的话,这将导致与观察到的行为相反的预期。

No explicit nulling out in either case, but note that in the no-for example the same memory location is actually reused, in contrast with the for example. This would, if anything, lead to the expectation opposite to the observed behavior.

根据我们从字节码学到的内容,尝试运行:

Based on what we learned from the bytecode, try running this:

public static void main(String[] args) {
  {
    long[] a1 = new long[5_000_000];
  }
  long[] a2 = new long[0];
  long[] a3 = new long[5_000_000];
}

没有抛出OOME 。注释掉 a2 的声明,它又回来了。我们分配更多,但占用更少?查看字节码:

No OOME thrown. Comment out the declaration of a2, and it is back. We allocate more, but occupy less? Look at the bytecode:

public static void main(java.lang.String[]);
  Code:
     0: ldc           #16                 // int 5000000
     2: istore_1      
     3: ldc           #16                 // int 5000000
     5: newarray       long
     7: astore_2      
     8: iconst_0      
     9: newarray       long
    11: astore_2      
    12: ldc           #16                 // int 5000000
    14: newarray       long
    16: astore_3      
    17: return        

位置2,用于 a1 ,重用于 a2 。 OP的代码也是如此,但现在我们用一个无害的零长度数组覆盖该位置,并使用另一个位置来存储对我们的巨大数组的引用。

The location 2, used for a1, is reused for a2. The same is true for OP's code, but now we overwrite the location with a reference to an innocuous zero-length array, and use another location to store the reference to our huge array.

Java语言规范未指定必须收集任何垃圾对象且仅限JVM规范表示在方法完成时,具有局部变量的框架作为一个整体被销毁。因此,我们目睹的所有行为都在书中。对象的不可见状态(在链接到 keppil 的文档中提到)只是描述在某些实现中和某些情况下会发生什么的一种方式,但是绝不是任何一种规范行为。

Java Language Specification does not specify that any garbage object must be collected and the JVM spec only says that the "frame" with local variables is destroyed as a whole upon method completion. Therefore all behaviors that we have witnessed are by the book. The invisible state of an object (mentioned in the document linked to by keppil) is just a way to describe what happens to go on in some implementations and under some circumstances, but is in no way any kind of canonical behavior.

这篇关于Java OutOfMemoryError奇怪的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆