gpt4 book ai didi

Java OutOfMemoryError 奇怪的行为

转载 作者:行者123 更新时间:2023-12-02 08:55:33 26 4
gpt4 key购买 nike

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

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

但是这个抛出了 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 运行此代码:

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

根据您是否进行热身或跳过它,它会吹或不吹。这是因为 JIT 代码正确地 null 输出了 var,而解释后的代码则不然。这两种行为在 Java 语言规范下都是可接受的,这意味着您将受到 JVM 的支配。

在 OS X 上使用 Java HotSpot(TM) 64 位服务器 VM(版本 23.3-b01,混合模式) 进行了测试。

字节码分析

使用 for 循环查看字节码(简单代码,没有 sum 变量):

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 示例中,实际上会重用相同的内存位置,这与 例如。如果有的话,这会导致与观察到的行为相反的期望。

一个转折...

根据我们从字节码中了解到的信息,尝试运行以下命令:

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的声明,又回来了。我们分配更多,但占用更少?看一下字节码:

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

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

总结一下...

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

关于Java OutOfMemoryError 奇怪的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30774402/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com