web-dev-qa-db-ja.com

Javaの循環参照

複雑で循環的な方法で相互に参照するクラスインスタンスの集約を考えると、ガベージコレクターがこれらのオブジェクトを解放できない可能性がありますか?

私はこれが過去のJVMの問題であることを漠然と思い出していますが、私はthoughtこれは何年も前に解決されました。それでも、jhatでの調査の結果、循環参照が、現在直面しているメモリリークの原因であることが判明しました。

注:私は常に、JVMが循環参照を解決し、そのような「ガベージの島」をメモリから解放できるという印象を受けていました。しかし、誰かが例外を見つけたかどうかを確認するためだけに、この質問を投げかけています。

48
Ryan Delucchi

循環参照の問題があるのは、非常に単純な実装だけです。ウィキペディアには、さまざまなGCアルゴリズムに関する優れた 記事 があります。詳細を知りたい場合は、(Amazon) ガベージコレクション:自動動的メモリ管理のアルゴリズム を試してください。 Java 1.2以降、優れたガベージコレクタがあり、1.5では非常に優れています。Java 6。

GCを改善するための難しい部分は、循環参照のような基本的なものではなく、一時停止とオーバーヘッドを減らすことです。

42
David G

ガベージコレクターは、ルートオブジェクトの場所(静的、スタック上のローカルなど)を認識しており、オブジェクトがルートから到達できない場合は再利用されます。彼らが到達可能である場合、彼らは固執する必要があります。

23
Rob Walker

Javaの循環参照 へのコメントから判断すると、ライアンは、おそらくブートストラップ/システムクラスローダーによってロードされたクラスからオブジェクトを参照するという罠に陥りました。すべてのクラスは、クラスをロードしたクラスローダーによって参照されるため、クラスローダーに到達できなくなった場合にのみガベージコレクションを実行できます。問題は、ブートストラップ/システムクラスローダーがガベージコレクションされることはないため、システムクラスローダーによってロードされたクラスから到達可能なオブジェクトもガベージコレクションできないことです。

この動作の理由は、JLSで説明されています。たとえば、第3版12.7 http://Java.Sun.com/docs/books/jls/third_edition/html/execution.html#12.7 です。

12
Alexander

私が正しく覚えている場合は、仕様に従ってJVMが収集できないものについてのみ保証があります(到達可能なすべてのもの)、収集するものではありません。

リアルタイムJVMを使用している場合を除き、最新のガベージコレクターは、複雑な参照構造を処理し、安全に削除できる「サブグラフ」を識別できる必要があります。より多くの研究アイデアが標準の(研究ではなく)VMに移行するにつれて、効率、待ち時間、およびこれを行う可能性は時間とともに向上します。

4
Uri

いいえ、少なくともSunの公式JVMを使用すると、ガベージコレクターはこれらのサイクルを検出し、外部からの参照がなくなるとすぐにメモリを解放できます。

3
Thilo

Java仕様では、ガベージコレクターはオブジェクトにガベージコレクションを実行できると述べていますどのスレッドからもアクセスできない場合。

到達可能とは、AからBにつながる参照、または参照のチェーンがあり、C、D、... Zを経由してすべての処理を実行できることを意味します。

JVMが2000年以降、物を収集しないことは問題ではありませんでしたが、実際の走行距離はさまざまです。

ヒント:Javaシリアライゼーションは、オブジェクトメッシュ転送を効率化するためにオブジェクトをキャッシュします。大きな一時オブジェクトが多数あり、すべてのメモリが占​​有されている場合は、シリアライザをリセットしてキャッシュをクリアします。

3
Tim Williscroft

循環参照は、あるオブジェクトが別のオブジェクトを参照し、他のオブジェクトが最初のオブジェクトを参照するときに発生します。例えば:

class A {
private B b;

public void setB(B b) {
    this.b = b;
}
}

class B {
private A a;

public void setA(A a) {
    this.a = a;
}
}

public class Main {
public static void main(String[] args) {
    A one = new A();
    B two = new B();

    // Make the objects refer to each other (creates a circular reference)
    one.setB(two);
    two.setA(one);

    // Throw away the references from the main method; the two objects are
    // still referring to each other
    one = null;
    two = null;
}
}

Javaのガベージコレクターは、循環参照がある場合にオブジェクトをクリーンアップするのに十分スマートですが、オブジェクトへの参照を持つライブスレッドはもうありません。したがって、このような循環参照があってもメモリリークは発生しません。

2
Rupesh

すでに言われていることを増幅するためだけに:

私が6年間取り組んできたアプリケーションは、最近Java 1.4からJava 1.6に変更されました。追加する必要があることがわかりました。以前は気付かなかったものへの静的参照はガベージコレクション可能でしたが、以前は静的参照が不要でした。

2
Paul Tomblin

GCの参照カウントは、この問題で悪名高いです。特に、Suns JVMは参照カウントGCを使用しません。

オブジェクトがヒープのルートから到達できない場合(通常、最低でもクラスローダーを介して他に何もない場合0)、オブジェクトは通常のJava中にコピーされないため破棄されます=新しいヒープへのGC。

2
Will Hartung

ガベージコレクターは非常に洗練されたソフトウェアです-巨大なJCKテストスイートでテストされています。完璧ではありませんが、Java compiler(javac)がすべてのクラスをコンパイルし、JVMがインスタンス化する限り、十分な可能性があります。

この場合も、このオブジェクトグラフのルートへの参照を保持している場合、メモリは解放されませんが、実行していることがわかっている場合は、問題ありません。

0
anjanb