web-dev-qa-db-ja.com

オブジェクトがガベージコレクションされないようにするにはどうすればよいですか?

オブジェクトがガベージコレクションされないようにするにはどうすればよいですか?

ファイナライズまたはファントム参照によるアプローチ、またはその他のアプローチはありますか?

私はインタビューでこの質問をされました。インタビュアーは、finalize()を使用できると提案しました。

32
Rahul Garg

参照を保持します。オブジェクトが時期尚早に収集されている場合は、アプリケーションの設計にバグがあることが症状です。

ガベージコレクターは、アプリケーションで参照のないオブジェクトのみを収集します。収集されたオブジェクトを自然に参照するオブジェクトがない場合は、なぜそれを維持する必要があるかを考えてください。

通常は参照を持たないが、オブジェクトを保持したい1つのユースケースは、シングルトンです。この場合、静的変数を使用できます。シングルトンの可能な実装の1つは次のようになります。

public class Singleton {
  private static Singleton uniqueInstance;

  private Singleton() {
    }

  public static synchronized Singleton getInstance() {
    if (uniqueInstance == null) {
      uniqueInstance = new Singleton();
    }
    return uniqInstance;
  }
}

編集:技術的には、ファイナライザのどこかに参照を保存できます。これにより、参照がなくなったとコレクターが再び判断するまで、オブジェクトが収集されなくなります。ただし、ファイナライザは多くても1回しか呼び出されないため、最初のコレクションの後でオブジェクト(スーパークラスを含む)をファイナライズする必要がないようにする必要があります。ただし、実際のプログラムではこの手法を使用しないことをお勧めします。 (それは私のような同僚がWTFを叫ぶことになります!?;)

  protected void finalize() throws Throwable {
    MyObjectStore.getInstance().store(this);
    super.finalize(); // questionable, but you should ensure calling it somewhere.
  }
32
Tobias

インタビュアーが探していたトリックの答えは、おそらくメモリリークを強制することによってガベージコレクションがオブジェクトを削除するのを防ぐことができるということを彼があなたに知ってほしいということでしょう。

明らかに、オブジェクトへの参照を長期間有効なコンテキストで保持している場合、それは収集されませんが、それはOPの採用担当者が尋ねたものではありません。これはfinalizeメソッドで発生するものではありません。

Finalizeメソッド内からガベージコレクションを防ぐためにできることは、Thread.yield();を呼び出す無限ループを作成することです(おそらく、空のループが最適化されないようにするためです)。

@Override
protected void finalize() throws Throwable { 
    while (true) { 
        Thread.yield(); 
    } 
} 

私がここで参照しているのは Elliot Back による記事で、この方法でメモリリークを強制することについて説明しています。

ちょうど別の方法で ファイナライズメソッドは悪です

9
CPerkins

最良の方法は nsafe を使用することですが、ByteBufferはいくつかのケースで可能な回避策となる場合があります。

「off-heap」メモリというキーワードも検索します。

安全ではない

ByteBufferに対する利点:

  • 直列化なしでオブジェクトを直接表現できるため、より高速
  • 境界チェックがないため、より高速
  • 明示的な割り当て解除制御
  • jVM制限を超えて割り当てることができる

しかし、働くのは簡単ではありません。この方法は次の記事で説明されています。

これらはすべて次の手順で構成されています。

  • unsafeにはないsizeof演算子が必要です。作成方法は次のように尋ねられました: Javaでは、オブジェクトのサイズを決定する最良の方法は何ですか? 。最良のオプションはおそらくinstrument AP​​Iですが、Jarを作成して特別なコマンドラインオプションを使用する必要があります...

  • sizeofを取得したら、Unsafe#allocateMemoryで十分なメモリを割り当てます。これは基本的にmallocであり、アドレスを返します

  • 通常のヒープオブジェクトを作成し、Unsafe#copyMemoryを使用して割り当てられたメモリにコピーします。これを行うには、ヒープ上のオブジェクトのアドレスとオブジェクトのサイズが必要です

  • 割り当てられたメモリを指すようにObjectを設定してから、Objectをクラスにキャストします。

    Unsafeを使用して変数のアドレスを直接設定することはできないようです。そのため、オブジェクトを配列またはラッパーオブジェクトにラップし、Unsafe#arrayBaseOffsetまたはUnsafe#objectFieldOffsetを使用する必要があります。

  • 完了したら、割り当てられたメモリをfreeMemoryで解放します

これをsegfaultにしない場合は、例を投稿します:-)

ByteBuffer

安全でない場合の利点:

  • Javaバージョンで安定しているが、Unsafeは壊れる可能性がある
  • 境界チェックを行うため、安全ではありません...安全ではないため、メモリリークとSIGSEGV

JLSによると

ダイレクトバッファの内容は、通常のガベージコレクションされたヒープの外に存在する場合があります。

プリミティブでの使用例:

ByteBuffer bb = ByteBuffer.allocateDirect(8);

bb.putInt(0, 1);
bb.putInt(4, 2);
assert bb.getInt(0) == 1;
assert bb.getInt(4) == 2;

// Bound chekcs are done.
boolean fail = false;
try {
    bb.getInt(8);
} catch(IndexOutOfBoundsException e) {
    fail = true;
}
assert fail;

関連スレッド:

それでもオブジェクトへの参照がある場合、ガベージコレクションは行われません。それへの参照がない場合は、気にする必要はありません。

つまり、ガベージコレクタはガベージを収集するだけです。任せてください。

3
Thomas Owens

finalizeメソッドがファイナライズされるオブジェクトへの参照を隠している場合は、あなたが参照している可能性があると思います。この場合( Java言語仕様 の読み取りが正しい場合)、finalizeメソッドは再実行されませんが、オブジェクトはまだガベージコレクションされません。

これは、偶然の場合を除いて、実際に行うことではありません。

2
Simon Nickerson

これは、面接のみの時間-あなたがそれを見ることになる質問の1つに聞こえます。 finalize()は、オブジェクトがガベージコレクションされているときに実行されるため、コレクションを防ぐために何かをそこに置くのはかなりおかしいでしょう。通常は、参照を保持するだけで十分です。

ファイナライザーで何かの新しい参照を作成するとどうなるかはわかりません。ガベージコレクターはすでに収集することを決定しているため、最終的にはnull参照になりますか?いずれにしても、悪い考えのようです。例えば.

public class Foo {
   static Foo reference;
  ...
  finalize (){ 
     reference = this; 
  }
}

私はこれがうまくいくか、うまくいくかもしれませんが、GC実装に依存しているか、または「不特定の動作」であるとは思いません。しかし、悪そうに見えます。

2
Steve B.

重要な点は、オブジェクトを指す実際の参照変数をnullに設定した場合ですが、そのオブジェクトを指すそのクラスのインスタンス変数はnullに設定されていません。オブジェクトは自動的にガベージコレクションの対象になります。オブジェクトをGCに保存する場合は、次のコードを使用してください...

public class GcTest {

    public int id;
    public String name;
    private static GcTest gcTest=null;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();

        System.out.println("In finalize method.");
        System.out.println("In finalize :ID :"+this.id);
        System.out.println("In finalize :ID :"+this.name);

        gcTest=this;

    }

    public static void main(String[] args) {

        GcTest myGcTest=new GcTest();
        myGcTest.id=1001;
        myGcTest.name="Praveen";
        myGcTest=null;

        // requesting Garbage Collector to execute.
        // internally GC uses Mark and Sweep algorithm to clear heap memory.
        // gc() is a native method in RunTime class.

        System.gc();   // or Runtime.getRuntime().gc();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("\n------- After called GC () ---------\n");
        System.out.println("Id :"+gcTest.id);
        System.out.println("Name :"+gcTest.name);


    }

}

出力:

Finalizeメソッドで。
ファイナライズ:ID:1001
in finalize:ID:Praveen

------- GCが呼び出された後()--------

Id:1001
名前:Praveen

1

彼らが行っているのは、finalizeを使用してリソースをプールに返し、リソースを保持している実際のオブジェクトがGCではないリソースプール(たとえば、ネットワーク/ db接続、またはスレッド)のパターンなのかどうかed。

愚かな例、Javaのような疑似コードであり、あらゆる種類の同期がない:

class SlowResourceInternal {
   private final SlowResourcePool parent;
   <some instance data>

   returnToPool() {
       parent.add(this);
   }
}

class SlowResourceHolder {
    private final SlowResourceInternal impl;

    <delegate actual stuff to the internal object>

    finalize() {
        if (impl != null) impl.returnToPool();
    }
}
1
Ed Handy

これを実現するには、3つの方法があります。1)Heap-Edenスペースサイズを大きくします。 2)静的参照を持つシングルトンクラスを作成します。 3)finalize()メソッドをオーバーライドし、そのオブジェクトを間接参照させないでください。

0
Satyendra

これにはパターンがあると思います。それが工場のパターンかどうかはわかりません。しかし、すべてのオブジェクトを作成し、それらへの参照を保持する1つのオブジェクトがあります。それらを使い終わったら、ファクトリでそれらを逆参照し、呼び出しを明示的にします。

0
Zoidberg