当前位置: 首页 > 知识库问答 >
问题:

Java OutOfMemoryError异常行为

贺雪松
2023-03-14

假设我们的最大内存为256M,为什么这段代码可以工作:

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

但这一个会发出声音吗?

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

共有2个答案

慕容星晖
2023-03-14

这是因为虽然a1不在括号后的范围内,但在方法返回之前,它处于称为“不可见”的状态。

大多数现代JVM不会在变量a1离开作用域后立即将其设置为null(实际上,内部括号是否存在甚至不会改变生成的字节码),因为它非常无效,通常也无关紧要。因此,在方法返回之前,a1不能被垃圾收集。

您可以通过添加行来检查此

a1 = null;

在括号内,这使程序运行良好。

“隐形”一词和解释取自这篇旧论文:http://192.9.162.55/docs/books/performance/1st_edition/html/JPAppGC.fm.html

沈凡
2023-03-14

为了保持透视,请考虑用<代码> -XMX64 M < /代码>运行此代码:

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的支配。

在OS X上使用Java HotSpot(TM)64位服务器虚拟机(构建23.3-b01,混合模式)进行测试。

查看带有for循环的字节码(简单代码,没有变量):

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

在这两种情况下都没有显式的nulling out,但请注意,在No示例中,同一个内存位置实际上是重复使用的,与示例相反。这可能会导致与观察到的行为相反的预期。

根据我们从字节码中学到的知识,尝试运行以下命令:

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];
}

不要扔东西。注释掉a2的声明,它就回来了。我们分配更多,但占用更少?看看字节码:

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的代码也是如此,但是现在我们用对一个无害的零长度数组的引用覆盖这个位置,并使用另一个位置来存储对我们巨大数组的引用。

Java语言规范没有指定必须收集任何垃圾对象,JVM规范只是说带有局部变量的“框架”在方法完成后作为一个整体被销毁。因此,我们所目睹的所有行为都是照本宣科的。对象的不可见状态(在keppil链接的文档中提到)只是描述在某些实现和某些情况下发生的事情的一种方式,但绝不是任何规范行为。

 类似资料:
  • 问题内容: 当运行上述代码片段时,在控制台中,我得到的输出为: 但我期望输出为 但是当在main()内部更改如下所述的逻辑时 我正在获得所需的输出 根据我的理解,TreeMap的entrySet()方法返回地图中包含的映射的集合视图。集合的迭代器以升序键顺序返回映射。那么,为什么在第一种情况下会发生这种情况? 任何建议都将受到高度赞赏。 问题答案: 因为在字典上小于。 这里有一个提示: 这是另一个

  • 这里有点小麻烦。我正在尝试编写一些代码来确定当前用户是否有:一个超过30天的帐户和0个woocommerce订单。我有这个顶部部分提取所有的订单属于用户在过去90天。 然而,它返回0个订单,即使我的测试用户有1个已完成的订单。我预感可能是这行:“before”=>date('y-m-d',strtotime('now'))所以我去掉了它,然后它返回“19个订单”,这简直是不可能的。print_r部

  • 当我尝试用命令从终端运行sikuli时 java-jar sikuli-setup.jar

  • 我在ios7和iOS8中都遇到了一些奇怪的行为。。 有时全屏显示X(POTRAIT全屏广告-我的应用程序仅为横向)。你点击X,就可以返回我的菜单了。 但有时会显示不带X的广告(LANDSCAPE FUllSCREEN AD)。如果您等待委托永远不会被调用。所以我尝试单击它以离开。然后它会显示另一个带有X的广告(LANDSCAPE FUllSCREEN AD)。所以我单击X。然后它会转到另一个广告(

  • 我的tmp目录无法通过php访问。因此,会话不能真正工作(他们没有),但在这种情况下,他们做了一些未知的原因。 这是一个场景, 我有一个页面index.php验证用户的凭据和设置会话变量,并将用户重定向到panel.php panel.php我有密码, 当我登录通过index.php,虽然会话数据无法读取,panel.php打开像它通常与会话 刷新页面时,会话消失,页面重定向! 对这种行为有什么解

  • 我有一个基于PHP的应用程序,它使用Azure队列。我的应用程序有一个cron,每20分钟在我的Web服务器上运行一次,它将多达2000条消息添加到Azure队列存储。它过去把这些消息放在一个队列中,但现在我使用两个不同的队列,或者把消息放在每个队列中。在每条消息添加到队列后,还有十分之一秒的Hibernate期,以避免节流。 我在Azure上有大约40个VM从这个队列中读取,处理每个消息,然后在