2

考虑以下类:

public class MemoryTest
{
  private long[] longArr; // Eclipse marks this unused

  public MemoryTest(int arrSize) {
    longArr = new long[arrSize*10];
  }

  public static void main(String[] args) {
    int numObjs = 10000;
    MemoryTest[] mts = new MemoryTest[numObjs];

    for(int i = 0; i < numObjs; i++) {
      // build a large number of objects which hold expensive, unused references
      mts[i] = new MemoryTest(i);
      //System.gc(); interestingly, uncommenting this line causes OOM earlier
      System.out.println("Built "+i+": "+Runtime.getRuntime().freeMemory()/1024+"KB");
    }

    System.out.println(Arrays.hashCode(mts)); // so elements cannot be GCed earlier
  }
}

这失败了:

... truncated output ...
Built 5236: 62070KB
Built 5237: 61661KB
Built 5238: 61252KB
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at MemoryTest.<init>(MemoryTest.java:6)
    at MemoryTest.main(MemoryTest.java:15)

显然这是一个人为的例子,但我想知道 GC/JIT 编译器是否可以整理这个longArr没有在任何地方使用的对象。从 Java 7 开始,现在似乎还没有整理好,但是如果编译器变得更聪明或者我们以某种方式更改示例,这可能会发生,还是 JVM 合同禁止它?

4

2 回答 2

2

即使您的代码longArr在编译时没有(似乎)访问任何地方,它仍然可以随时通过反射访问,这是 GC/JIT 编译器无法猜测的。

唯一可以删除的未使用变量是本地变量(它们的引用可以在分配后立即清除)。事实上,Eclipse 编译器甚至允许在编译时删除它们(请参阅首选项 > Java > 编译器 > 保留未使用的局部变量)。

于 2013-03-04T15:42:20.900 回答
0

longArr不在任何地方使用;但是您正在创建您的类MemoryTest的对象。这意味着属性longArr正在为每个对象初始化。

因此,如果对您的类的对象的引用不为空;那么不要认为有任何方法可以 GC 这个属性。longArr将始终通过对象绑定;所以只有当对象被 GC 时它才能被清理。

于 2013-03-04T15:35:48.127 回答