1

以下のコードでは、1 つのオブジェクトのみが実行時にヒープ メモリから消去されます ( を呼び出すことによりSystem.gc())。1 つのオブジェクトだけがリサイクルされるのはなぜですか?

 class A{
    A a;
    public A(A a){
      this.a=a;
    }
    public static void main(String args[]){
      A a1=null;
      A a2=new A(new A(null));
      A a3=new A(a2);
      a1=a3;
      a1.a=new A(null);
      a2.a=null;  //Line12
      System.gc();
    }
  }
4

3 に答える 3

3

Java で正確な GC コレクション時間をピン留めしようとするのは恐ろしいことだと思いますが、とにかく ..

.. Java ガベージ コレクションは、オブジェクトの到達可能性で機能します。オブジェクトが強力に到達可能な場合、つまり、ローカル変数を含む到達可能性または既知のルートから任意のパスでアクセスできる場合、オブジェクトは再利用できません1

ここで、W、X、Y、Z は A の異なるインスタンスを表します。特定の A を示すために使用する構文は、メンバー変数を参照するinstance {a-> instance}場所です。aそれぞれが異なるインスタンスnewを作成し、オブジェクト値の割り当てによって新しいオブジェクトが作成されないことに注意してください (そのため、現在 a1 と a3 によって共有されている同じ Y オブジェクトが割り当て中に変更されます)。a1.a=new A(null)

  A a1=null;                // a1 = null
  A a2=new A(new A(null));  // a2 = W {a-> X}
  A a3=new A(a2);           // a3 = Y {a-> W}    
  a1=a3;                    // a1 = a3 = Y {a-> W}
  a1.a=new A(null);         // a1 = a3 = Y {a-> Z}
  a2.a=null;                // a2 = W {a-> null}
  System.gc();

  // Then:
  // a1 = a3 = Y {a-> Z}    (Expanding: Y {a-> Z {a-> null}})
  // a2 = W {a-> null}

したがって、変数 a1 と a3 (到達可能性のルート) は最後に"参照"Y {a->Z}され、a2 は "参照" されW {a->null}ます。これは、W、Y、および Z はすべてまだ強力に到達可能 (Z は Y を介して強力に到達可能) であり、Java ランド2での再利用の対象とは見なされないことを意味します。

X のみがラインで到達できなくなりましたSystem.gc()。ただし、これは、明示的な GC 呼び出しであっても、X がガベージ コレクションされることを意味するものではありませ。これは、X が将来のある時点で再利用される資格があることを意味するだけです。

もちろん、関数が終了するとすぐに、すべてのローカル変数は到達可能性のルートではなくなり、どの A オブジェクトも強く到達可能ではなくなり、すべてが適格になります:)


1オブジェクトの到達可能性を使用して GC を議論することは、従来のマーク & スイープ ベースのアプローチ (JVM で一般的に見られる) を使用しない場合でも機能します。これは、正常に機能する GC システムに同じルールが適用されるためです。アクセスすると、再利用できません。逆に、オブジェクトにアクセスできない場合は、再利用する必要があります。

2到達可能性ルートを決定するルールは、言語/ランタイムによって異なります。たとえば、特定のエッジ ケースでの C#/CLR 処理には微妙な違いがあります。

于 2013-09-23T19:33:41.557 に答える
3

他の人が指摘したように、オブジェクトは、変数の値として直接的にも間接的にもアクセスできない場合に収集できます。これは、別のアクセス可能なオブジェクトがそれへの参照を持っているためです。コードを数行ずつ見ていき、オブジェクト参照グラフがどのように変化するかを見てみましょう。

  A a1=null;               // 1
  A a2=new A(new A(null)); // 2
  A a3=new A(a2);          // 3
  a1=a3;                   // 4

行 1 はあまり機能しません。それを削除して、行 4 を に変更してもA a1 = a3、結果に違いはありません。2 行目ではa2、 の値を の新しいインスタンス ( Aα と呼びます) への参照に設定します。そのaフィールドは、 の 2 番目の新しいインスタンス ( Aβ と呼びます) への参照です。3 行目は の新しいインスタンスを作成し、Aそれを γ と呼びます。そのaフィールドは α への参照です。したがって、α、β、および γ のすべてが参照されます。行 4 は、a1の値を γ への参照にします。ご了承ください:

  • α は として直接アクセス可能a2であり、γ がアクセス可能である限り間接的にアクセス可能です。
  • β は、α にアクセスできる限り、α を介して間接的にアクセスできます。と
  • γ はa1およびとして直接アクセスできますa3

最初の記憶状態

次、

  a1.a=new A(null);        // 5

a1.a = new A(null)γ のaフィールドを の新しいインスタンスになるように更新しますA。これを δ と呼びます。今:

  • α は として直接アクセスできa2、それ以外から間接的にアクセスすることはできません。
  • β は、γ を介してのみ間接的にアクセスできます。
  • γ はa1およびとして直接アクセスできますa3。と
  • δ は、α を介してのみ間接的にアクセスできます。

第二の記憶状態

ついに、

  a2.a=null;               // 6

ここで、β への最後の残りの参照が削除されます。α、γ、およびδへの参照がまだあります。

  • α は次のように直接アクセスできますa2
  • β には、直接または間接的にアクセスすることはできません。
  • γ はa1およびとして直接アクセスできますa3。と
  • δ は、γ を介してのみ間接的にアクセスできます。

最終記憶状態

βは全然アクセスできないのでガベージコレクションの候補です。のjavadocはSystem.gc()言う:

ガベージ コレクタを実行します。このメソッドを呼び出すことは、Java 仮想マシンが未使用のオブジェクトをリサイクルして、現在占有しているメモリをすばやく再利用できるようにすることを示唆しています。メソッド呼び出しから制御が戻ると、仮想マシンは破棄されたすべてのオブジェクトをリサイクルするために最善を尽くしています。

したがって、私たちが到達したとき

  System.gc(); // 7

システムはβ を収集する場合がありますが、必須ではありません。β は「廃棄されたすべてのオブジェクトをリサイクルするための最善の努力」によって収集されると想定するのがおそらく安全ですが、それは保証ではありません。

于 2013-09-23T19:41:44.713 に答える
0

以下のコメントを確認してください。

public static void main(String args[]){
  A a1=null;
  A a2=new A(new A(null));  //a2.a = new A(null); //line 3
  A a3=new A(a2);           //a3.a = a2
  a1=a3;                    //a1 = a3
  a1.a=new A(null);         //a1.a = a3.a = null; a2 is still referenced
  a2.a=null;  //Line12      //a2.a = null => object at line 3 is cleared after gc
                            //because it is not referenced anymore by any variable.
  System.gc();
}
于 2013-09-23T19:34:22.973 に答える