web-dev-qa-db-ja.com

Javaガベージコレクションは循環参照でどのように機能しますか?

私の理解では、Javaのガベージコレクションは、他のオブジェクトがそのオブジェクトを「ポイント」していない場合、そのオブジェクトをクリーンアップします。

私の質問は、次のようなものがあるとどうなるかです。

class Node {
    public object value;
    public Node next;
    public Node(object o, Node n) { value = 0; next = n;}
}

//...some code
{
    Node a = new Node("a", null), 
         b = new Node("b", a), 
         c = new Node("c", b);
    a.next = c;
} //end of scope
//...other code

ab、およびcはガベージコレクションされる必要がありますが、それらはすべて他のオブジェクトによって参照されています。

Javaガベージコレクションはこれをどのように処理しますか?(または単にメモリドレインですか?)

145
AlexeyMK

JavaのGCは、ガベージコレクションルートから始まるチェーンを介してオブジェクトに到達できない場合、オブジェクトを「ガベージ」と見なします。したがって、これらのオブジェクトは収集されます。オブジェクトが相互に指し合ってサイクルを形成している場合でも、ルートから切り離された場合、それらは依然としてゴミです。

厄介な詳細については、「付録A:ガベージコレクションについての真実 Javaプラットフォームパフォーマンス:戦略と戦術 」の到達不能オブジェクトに関するセクションを参照してください。

147
Bill the Lizard

yes Javaガベージコレクターは循環参照を処理します!

How?

ガベージコレクションルート(GCルート)と呼ばれる特別なオブジェクトがあります。これらは常に到達可能であり、独自のルートにあるオブジェクトも同様です。

単純なJavaアプリケーションには次のGCルートがあります。

  1. Mainメソッドのローカル変数
  2. メインスレッド
  3. メインクラスの静的変数

enter image description here

使用されなくなったオブジェクトを判別するために、JVMはマークアンドスイープアルゴリズムと呼ばれるものを断続的に実行します。次のように動作します

  1. アルゴリズムは、GCルートから始まるすべてのオブジェクト参照をトラバースし、見つかったすべてのオブジェクトを生きているとマークします。
  2. マークされたオブジェクトによって占有されていないすべてのヒープメモリが回収されます。単に空きとしてマークされ、基本的には未使用のオブジェクトが一掃されます。

そのため、GCルートから到達できないオブジェクトがある場合(自己参照または循環参照であっても)、ガベージコレクションの対象になります。

もちろん、プログラマがオブジェクトの逆参照を忘れると、メモリリークが発生することがあります。

enter image description here

ソース: Javaメモリ管理

121
Aniket Thakur

ガベージコレクターは、CPUレジスタ、スタック、グローバル変数など、常に「到達可能」と見なされる場所の「ルート」セットから始まります。それらの領域でポインターを見つけ、それらが指すすべてを再帰的に見つけることで機能します。それがすべて見つかると、everything elseはごみです。

もちろん、主に速度のために、かなりの数のバリエーションがあります。たとえば、最新のガベージコレクターは「世代別」です。つまり、オブジェクトを世代に分割します。オブジェクトが古くなると、ガベージコレクターは、そのオブジェクトがまだ有効かどうかを判断しようとする時間をますます長くします。 -長い間住んでいれば、さらに長く生き続ける可能性がかなり高いと仮定し始めただけです。

それにもかかわらず、基本的な考え方は同じままです。それは、当然のことながら、使用できる可能性のあるルートセットから開始し、すべてのポインタを追跡して、他に使用可能なものを見つけることに基づいています。

興味深いことに、ガベージコレクターのこの部分と、リモートプロシージャコールなどのオブジェクトをマーシャリングするためのコードとの類似性の程度に、人々が驚かされることがよくあります。いずれの場合も、オブジェクトのルートセットから開始し、参照する他のすべてのオブジェクトを見つけるためにポインターを追跡しています...

11
Jerry Coffin

あなたは正しいです。説明するガベージコレクションの特定の形式は、「参照カウント」と呼ばれます。最も単純な場合の動作方法(概念的には、少なくとも、最新の参照カウントの実装は実際にはまったく異なる方法で実装されます)は、次のようになります。

  • オブジェクトへの参照が追加されるたびに(たとえば、変数またはフィールドに割り当てられ、メソッドに渡されるなど)、参照カウントは1ずつ増加します
  • オブジェクトへの参照が削除されると(メソッドが戻り、変数がスコープ外になり、フィールドが別のオブジェクトに再割り当てされるか、フィールドを含むオブジェクト自体がガベージコレクションされます)、参照カウントは1ずつ減少します
  • 参照カウントが0に達するとすぐに、オブジェクトへの参照はなくなります。つまり、誰もそれを使用できなくなります。したがって、ガベージであり、収集できます。

そして、この単純な戦略にはまさにあなたが説明する問題があります.AがBを参照し、BがAを参照する場合、その両方の参照カウントはneverが1未満になる可能性があり、収集されないことを意味します。

この問題に対処するには、4つの方法があります。

  1. それを無視します。十分なメモリがある場合、サイクルは小さく頻度が少なく、ランタイムは短いので、単にサイクルを収集しないで済むかもしれません。シェルスクリプトインタープリターについて考えてみましょう。通常、シェルスクリプトは数秒間だけ実行され、多くのメモリを割り当てません。
  2. 参照カウントのガベージコレクタをanotherサイクルに問題のないガベージコレクタと組み合わせます。たとえば、CPythonはこれを行います。CPythonのメインガベージコレクタは参照カウントコレクタですが、時々、トレースガベージコレクタが実行されてサイクルを収集します。
  3. サイクルを検出します。残念ながら、グラフ内のサイクルを検出するのはかなり高価な操作です。特に、トレースコレクターとほぼ同じオーバーヘッドが必要なので、それらの1つを使用することもできます。
  4. あなたと私は素朴な方法でアルゴリズムを実装しないでください:1970年代以来、サイクル検出と参照カウントを単一の操作で巧妙な方法で組み合わせ、非常に安価な複数の非常に興味深いアルゴリズムが開発されてきました別々に、またはトレースコレクターを実行します。

ちなみに、otherガベージコレクターを実装する主要な方法(および、上記の2、3回のことを既に示唆しました)はtracingです。トレースコレクタは、到達可能性の概念に基づいています。まず、root setであることがわかっているalways到達可能(たとえば、グローバル定数、またはObjectクラス、現在のレキシカルスコープ、現在のスタックフレーム)そしてそこからtraceルートセットから到達可能なすべてのオブジェクト、ルートセットから到達可能なオブジェクトから到達可能なすべてのオブジェクトなど、推移的閉包が得られるまで。そのクロージャーでnotであるものはすべてゴミです。

サイクルはそれ自体内でのみ到達可能であり、ルートセットからは到達できないため、収集されます。

10
Jörg W Mittag

Java GCは実際には説明どおりに動作しません。「GCルート」と呼ばれることが多いオブジェクトの基本セットから開始し、ルートからは到達できません。
GCルートには次のようなものが含まれます。

  • 静的変数
  • 現在実行中のスレッドのスタックにあるローカル変数(該当するすべての「this」参照を含む)

したがって、あなたの場合、ローカル変数a、b、およびcがメソッドの最後にスコープから外れると、3つのノードのいずれかへの参照を直接または間接的に含むGCルートはなくなります。ガベージコレクションの対象になります。

豆腐ビールのリンクには、必要に応じて詳細が記載されています。

6
Sbodd

この記事 (使用できなくなりました)ガベージコレクターについて詳しく説明します(概念的には、いくつかの実装があります)。投稿に関連する部分は「A.3.4到達不能」です。

A.3.4到達不能オブジェクトは、それに対する強力な参照が存在しなくなると到達不能状態になります。オブジェクトが到達不能である場合、それはコレクションの候補です。言い回しに注意してください:オブジェクトがコレクションの候補であるからといって、すぐに収集されるわけではありません。 JVMは、オブジェクトがメモリを消費することがすぐに必要になるまで、収集を遅らせることができます。

4
TofuBeer

ガベージコレクションは、通常、「他のオブジェクトがそのオブジェクトを「指している」場合にのみ、オブジェクトをクリーンアップする」ことを意味しません(参照カウント)。ガベージコレクションとは、おおよそ、プログラムから到達できないオブジェクトを見つけることを意味します。

したがって、あなたの例では、a、b、およびcがスコープから外れると、これらのオブジェクトにアクセスできなくなるため、GCによって収集できます。

0
Amnon

ビルはあなたの質問に直接答えました。アムノンが言ったように、ガベージコレクションの定義は単なる参照カウントです。マークアンドスイープやコピーコレクションなどの非常に単純なアルゴリズムでも、循環参照を簡単に処理できることを追加したかっただけです。だから、それについて魔法はありません!

0
Claudiu