添加 for-loop 可防止 OutOfMemoryError

2022-09-01 14:35:27

当我删除 for 循环时,我得到一个 .当我使用for-loop时,我没有得到任何错误。OutOfMemoryError

任何人都可以帮助我理解这种行为吗?

public class JavaMemoryPuzzlePolite {
    private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6);

    public void f() {
        {
            System.out.println(dataSize);
            byte[] data = new byte[dataSize];
        }
        for (int i = 0; i < 1; i++) {
            System.out.println("Please be so kind and release memory");
        }
        System.out.println(dataSize);
        byte[] data2 = new byte[dataSize];
    }

    public static void main(String[] args) {
        JavaMemoryPuzzlePolite jmp = new JavaMemoryPuzzlePolite();
        jmp.f();
    }
}

答案 1

该方法在解释的帧中执行。解释的帧的行为与 JIT 编译的帧不同。以下是它在没有 for 循环的伪代码中的外观:f()

1. Allocate dataSize bytes of memory
2. Store it into variable slot #1
3. Allocate dataSize bytes of memory
4. Store it into variable slot #1

所以你有步骤#3,因为旧数组仍然驻留在变量#1中。但是,添加 for 循环(实际上添加一个变量)会使事情变得不同:OutOfMemoryErrorbyte[]i

1. Allocate dataSize bytes of memory
2. Store it into variable slot #1
3. Store 0 to slot #1 (thus byte[] array is now eligible for GC)
4. Do the for loop
5. Allocate dataSize bytes of memory
6. Store it into variable slot #2

在这里,当您在步骤 #5 中分配新数组时,第一个数组已经可以被垃圾回收。

请注意,JIT 编译器的行为可能更聪明,并在第一个数组变得未使用时将其从变量中取消链接(在您的特定情况下,它根本不会分配它)。

另请注意,在您的特定情况下,结果取决于java编译器。ECJ(Eclipse编译器)足够聪明,根本不会将第一个数组存储到变量中,因为它没有被使用。因此,即使没有 for 循环,您也不会进入 ECJ 编译的类。OutOfMemoryError

有关更多详细信息,您可以查看实用程序提供的字节码反汇编输出,并了解如何重用变量插槽。javap


答案 2