添加For循环可防止OutOfMemoryError

当我删除for循环时,我得到一个OutOfMemoryError。 当我使用for循环时,我没有得到错误。 任何人都可以帮助我理解这种行为吗?

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

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

 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上有OutOfMemoryError ,因为旧的byte[]数组仍然存在于变量#1中。 但是添加for循环(实际上添加一个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实用程序提供的字节码反汇编输出,并查看如何重用变量槽。