9

これは非常に基本的な質問です。C++ と Java を使用して定式化しますが、実際には言語に依存しません。C++ のよく知られた問題を考えてみましょう。

struct Obj
{
    boost::shared_ptr<Obj> m_field;
};

{
    boost::shared_ptr<Obj> obj1(new Obj);
    boost::shared_ptr<Obj> obj2(new Obj);
    obj1->m_field = obj2;
    obj2->m_field = obj1;
}

これはメモリリークであり、誰もが知っています:)。解決策もよく知られています。弱いポインターを使用して、「参照カウントのインターロック」を解除する必要があります。この問題は原理的に自動的に解決できないことも知られています。それを解決するのは、プログラマの責任です。

しかし、良い点があります。プログラマーは refcount 値を完全に制御できます。デバッガーでプログラムを一時停止し、obj1、obj2 の refcount を調べて、問題があることを理解できます。また、オブジェクトのデストラクタにブレークポイントを設定して、破壊の瞬間を観察する (またはオブジェクトが破壊されていないことを確認する) こともできます。

私の質問は、Java、C#、ActionScript、およびその他の「ガベージ コレクション」言語に関するものです。私は何かが欠けているかもしれませんが、私の意見では

  1. オブジェクトの参照カウントを調べさせない
  2. オブジェクトが破棄されたときに通知しない (オブジェクトが GC に公開されたとき)

これらの言語は、プログラマーがメモリをリークすることを許可していないため、これらの言語は優れているとよく耳にします。私が理解している限りでは、それらはメモリ管理の問題を隠し、解決を困難にするだけです。

最後に、質問自体:

ジャワ:

public class Obj
{
    public Obj m_field;
}

{
     Obj obj1 = new Obj();
     Obj obj2 = new Obj();
     obj1.m_field = obj2;
     obj2.m_field = obj1;
}
  1. メモリリークですか?
  2. はいの場合: どうすれば検出して修正できますか?
  3. いいえの場合:なぜですか?
4

6 に答える 6

2

Java には、独自のメモリ管理戦略があります。すべて (いくつかの特定のものを除く) はヒープに割り当てられ、GC が機能するまで解放されません。

例えば:

public class Obj {
    public Object example;
    public Obj m_field;
}

public static void main(String[] args) {
    int lastPrime = 2;
    while (true) {
        Obj obj1 = new Obj();
        Obj obj2 = new Obj();
        obj1.example = new Object();
        obj1.m_field = obj2;
        obj2.m_field = obj1;
        int prime = lastPrime++;
        while (!isPrime(prime)) {
            prime++;
        }
        lastPrime = prime;
        System.out.println("Found a prime: " + prime);
    }
}

C は、両方の 'obj' のメモリを手動で解放することを要求することでこの状況を処理します。C++ は 'obj' への参照をカウントし、それらがスコープ外になると自動的に破棄します。Java は、少なくとも最初はこのメモリを解放しません。

Java ランタイムは、メモリの使用量が多すぎると感じるまでしばらく待機します。その後、ガベージコレクターが作動します。

外側のループが 10,000 回繰り返された後、Java ガベージ コレクタがクリーンアップを決定したとします。この時点で、10,000 個のオブジェクトが作成されています (C/C++ では既に解放されているはずです)。

外側のループには 10,000 回の反復がありますが、新しく作成された obj1 と obj2 のみがコードによって参照される可能性があります。

これらは、参照される可能性のあるすべてのオブジェクトを見つけるために Java が使用する GC の「ルート」です。次に、ガベージ コレクターはオブジェクト ツリーを再帰的に反復し、'example' をガベージ コレクター ルートへの中毒でアクティブとしてマークします。

これらの他のすべてのオブジェクトは、ガベージ コレクターによって破棄されます。これにはパフォーマンスの低下が伴いますが、このプロセスは大幅に最適化されており、ほとんどのアプリケーションにとって重要ではありません。

C++ とは異なり、GC ルートから到達可能なオブジェクトのみが存続するため、参照循環についてまったく心配する必要はありません。

Java アプリケーションではメモリ (すべての反復からオブジェクトを保持するリストを考えてください) について心配する必要がありますが、それは他の言語ほど重要ではありません。

デバッグに関して: 高いメモリ値をデバッグするという Java の考え方は、特別な「メモリ アナライザー」を使用して、ヒープ上に残っているオブジェクトを見つけ出し、何が何を参照しているかを気にする必要はありません。

于 2015-12-24T02:04:48.197 に答える