Java Garbage Collectionは循環参照とどのように連携しますか?


161

私の理解では、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ガベージコレクションはこれをどのように処理しますか?(または単にメモリドレインですか?)


1
参照:stackoverflow.com/questions/407855/…、特に@gnudからの2番目の回答。
Seth

回答:


161

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

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


14
そのためのリファレンスはありますか?それをテストするのは難しいです。
タンジェンス2009

5
リファレンスを追加しました。オブジェクトのfinalize()メソッドをオーバーライドして、オブジェクトがいつ収集されるかを確認することもできます(ただし、これはfinalize()を使用することをお勧めする唯一のことです)。
トカゲに請求する

1
最後のコメントを明確にするために...オブジェクトの一意のIDを出力するdebug printステートメントをfinalizeメソッドに挿入します。相互参照するすべてのオブジェクトが収集されるのを確認できます。
トカゲに請求する

4
「...認識できるほど賢い...」というのは混乱を招きます。GCはサイクルを認識する必要はありません-それらは単に到達不可能であり、それゆえゴミです
Alexander Malakhov

86
@tangens「そのためのリファレンスはありますか?」ガベージコレクションについての議論。ベスト。パン。ずっと。
のMichałKosmulski

139

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

How?

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

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

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

ここに画像の説明を入力してください

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

  1. アルゴリズムは、GCルートから始めてすべてのオブジェクト参照を走査し、見つかったすべてのオブジェクトを生きているとマークします。
  2. マークされたオブジェクトによって占有されていないヒープメモリはすべて解放されます。単純にフリーとしてマークされ、基本的に未使用のオブジェクトはスイープされます。

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

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

ここに画像の説明を入力してください

ソース:Javaメモリ管理


3
完璧な説明!ありがとう!:)
Jovan Perovic 2015

その本をリンクしてくれてありがとう。これには、このトピックやその他のJava開発トピックに関するすばらしい情報が満載です。
Droj

14
最後の図では、到達できないオブジェクトがありますが、到達可能なオブジェクトセクションにあります。
La VloZ Merrill、

13

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

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

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

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


あなたが説明しているのはトレーシングコレクターです。他の種類のコレクターがあります。この説明で特に興味深いのは、参照カウントコレクターです。これ、サイクルで問題が発生する傾向があります。
イェルクWミッターク

@JörgW Mittag:確かに真実です-参照カウントを使用する(かなり最新の)JVMは知りませんが、元の質問に大きな違いをもたらす可能性は(少なくとも私には)ないようです。
Jerry Coffin

@JörgW Mittag:少なくともデフォルトでは、Jikes RVMは現在リージョンベースのトレースコレクターであるImmixコレクターを使用していると思います(参照カウントも使用します)。その参照カウントを参照しているのか、トレースなしで参照カウントを使用している別のコレクターを参照しているのかはわかりません(Immixが「リサイクラー」を呼び出すのを聞いたことがないので、後者を推測します)。
ジェリーコフィン

私は少し混乱しました:リサイクラーはハラペーニョで実装されていました(そうでしたか?)、私が考えていたアルゴリズム、それはジケスで実装されていました(そうでしたか?)はUlterior Reference Countingです。もちろん、Jikesがこれまたはそのガベージコレクターを使用すると言っても、Jikes、特にMMtkは同じJVM内で異なるガベージコレクターを迅速に開発およびテストするように特別に設計されているためです。
イェルクWミッターク

2
Ulterior Reference Countingは、2007年にImmixを設計したのと同じ人々によって2003年に設計されたため、おそらく後者が前者に取って代わりました。URCは他の戦略と組み合わせることができるように特別に設計されており、実際にURCの論文では、URCはトレースと参照カウントの利点を組み合わせたコレクターへの足がかりに過ぎないと明記されています。Immixはそのコレクターだと思います。とにかく、リサイクラは、純粋なそれにもかかわらず検出することができる参照カウントコレクタ、及び収集サイクル:WWW.Research.IBM.Com/people/d/dfb/recycler.html
イェルクWミッターク

13

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

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

そして、この単純な戦略には、あなたが非難する問題があります。AがBを参照し、BがAを参照する場合、それらの参照カウントは両方とも決して、彼らは収集されません飽きないだろうことを意味する、1未満になることはありませ。

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

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

ちなみに、ガベージコレクターを実装するもう1つの主要な方法(および上記で数回示唆したとおり)はトレースです。トレーシングコレクターは到達可能性の概念に基づいています。常に到達可能であることがわかっているいくつかのルートセット(たとえば、グローバル定数、またはクラス、現在のレキシカルスコープ、現在のスタックフレーム)から始め、そこからルートセットから到達可能なすべてのオブジェクトを追跡します。ルートセットなどから到達可能なオブジェクトから到達可能なすべてのオブジェクト。そのクロージャにないものはすべてゴミです。Object

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


1
質問はJava固有なので、Javaは参照カウントを使用しないため、問題が存在しないことを言及する価値があると思います。また、ウィキペディアへのリンクは「さらに読書」として役立つだろう。そうでなければ素晴らしい概要!
Alexander Malakhov

Jerry Coffinの投稿へのコメントを読んだばかりなので、確信が持てません:)
Alexander Malakhov

8

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

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

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

TofuBeerのリンクには、必要に応じて詳細が記載されています。


「...現在実行中のスレッドのスタックに...」他のスレッドのデータを破壊しないために、すべてのスレッドのスタックをスキャンしていませんか?
Alexander Malakhov

6

この記事(入手不可)は、ガベージコレクターについて深く掘り下げています(概念的には...いくつかの実装があります)。投稿に関連する部分は「A.3.4到達不能」です。

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


1
そのセクションへの直接リンク
Alexander Malakhov

1
リンクは利用できなくなりました
titus

1

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

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


「ガベージコレクションとは、おおよそプログラムから到達できないオブジェクトを見つけることを意味します」。ほとんどのGCアルゴリズムでは、実際にはその逆です。GCのルートから始めて、見つけられるものを確認します。残りは参照されていないゴミと見なされます。
Fredrik、

1
参照カウント、ガベージコレクションの2つの主要な実装戦略の1つです。(もう1つはトレースです。)
JörgW Mittag

3
@ヨルク:今日のほとんどの時間、ガベージコレクターについて人々が話すとき、彼らはある種のマークアンドスイープアルゴリズムに基づくコレクターを指します。参照カウントは、ガベージコレクターがない場合、通常は行き詰まっています。参照カウントは、ある意味ではガベージコレクション戦略ですが、その上に構築されたgcは現在存在しません。これは、GC戦略であると言っても、実際にはもはやGCではないため、人々を混乱させるだけです。戦略ですが、メモリを管理する別の方法です。
Fredrik、

1

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.