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)

longArr明らかにこれは不自然な例ですが、GC/JIT コンパイラーがどこにも使用されていないこのオブジェクトを片付けることができるかどうか疑問に思っていました。Java 7 の時点では、これを整理していないようですが、コンパイラがよりスマートになるか、何らかの方法で例を変更すると、これが発生する可能性がありますか、それとも JVM によって契約上禁止されていますか?

4

2 に答える 2

2

コードがコンパイル時にどこにもアクセスしない(そう思われる)場合longArrでも、リフレクションによっていつでもアクセスできる可能性があります。これは、GC/JITコンパイラが推測できないことです。

削除できる未使用の変数はローカル変数のみです(参照は割り当てられるとすぐにクリアできます)。実際、Eclipseコンパイラーでは、コンパイル時にそれらを削除することもできます(「設定」>「Java」>「コンパイラー」>「未使用のローカル変数を保持する」を参照)。

于 2013-03-04T15:42:20.900 に答える
0

longArrはどこにも使用されていません。ただし、クラスMemoryTestのオブジェクトを作成しています。これは、オブジェクトごとに属性longArrが初期化されていることを意味します。

したがって、クラスのオブジェクトへの参照が null でない場合。次に、この属性を GC する方法があるとは思わないでください。longArrは常にオブジェクトを介して結合されます。そのため、オブジェクトがGCされたときにのみクリーンアップできます。

于 2013-03-04T15:35:48.127 に答える