「こんにちは! ガベージ コレクションについてもう一度小さなレッスンをすることにしました。」

ご存知のとおり、Java マシン自体がオブジェクトが不要になった時点を監視し、削除します。

「ええ。あなたとリシがそのことについて以前教えてくれました。詳細は覚えていません。」

「わかりました。それではもう一度考えてみましょう。」

ガベージコレクション - 1

「オブジェクトが作成されるとすぐに、JVM はそのオブジェクトにメモリを割り当てます。オブジェクトへの関心は参照変数を使用して監視されます。オブジェクトは ガベージ コレクション中に削除できます。つまり、オブジェクトを参照する変数がない場合にメモリが解放される手順です。反対です。

「ガベージ コレクターについて少し説明してください。それが何で、どのように機能するのか。」

「OK。以前はガベージ コレクションがメイン スレッドで行われていました。5 分ごと、またはそれ以上の頻度で行われていました。空きメモリが不足すると、Java マシンはすべてのスレッドを一時停止し、未使用のオブジェクトを削除していました。」

「しかし、このアプローチは現在廃止されています。次世代のガベージ コレクターは舞台裏で別のスレッドで動作します。これは同時ガベージ コレクションと呼ばれます。」

「なるほど。オブジェクトを削除するかどうかは、具体的にどのように決定されるのですか?」

「オブジェクトへの参照の数を数えるだけではあまり効果的ではありません。相互に参照しているオブジェクトが存在する可能性がありますが、他のオブジェクトからは参照されていません。」

「そこで、Java は異なるアプローチをとります。Java は、オブジェクトを到達可能と到達不能に分けます。 オブジェクトは、別の到達可能 (生きている) オブジェクトによって参照されている場合、到達可能 (生きている) になります。到達可能性はスレッドから決定されます。実行中のスレッドは常に到達可能 (生きている) とみなされます。たとえ誰も参照しなかったとしても。」

「わかりました。分かったと思います。」

「実際のガベージ コレクション、つまり不要なオブジェクトの削除はどのように行われるのでしょうか?」

「それは簡単です。Java では、慣例によりメモリが 2 つの部分に分割されます。ガベージ コレクションの時間になると、すべての生きている (到達可能な) オブジェクトがメモリの別の部分にコピーされ、古いメモリはすべて解放されます。」

「これは興味深いアプローチです。参照をカウントする必要はありません。到達可能なオブジェクトをすべてコピーすれば、それ以外はすべてゴミになります。」

「それよりも少し複雑です。Java プログラマは、オブジェクトが通常 2 つのカテゴリに分類されることに気付きました。1 つは長寿命 (プログラムの実行中ずっと存在する) と、短寿命 (メソッド内および「ローカル」の実行に必要なオブジェクト) です。 " オペレーション)。"

「寿命の長いオブジェクトを寿命の短いオブジェクトから分離しておいた方がはるかに効率的です。これを行うには、オブジェクトの寿命を判断する方法を考え出す必要がありました。」

「そこで、彼らはすべてのメモリを«世代»に分割しました。第一世代のオブジェクト、第二世代のオブジェクトなどが存在します。メモリがクリアされるたびに、世代カウンタは1ずつ増加します。特定のオブジェクトが複数の世代に存在する場合、それらは長命であると記録されています。」

「今日、ガベージ コレクターは Java の非常に複雑かつ効率的な部分です。その部分の多くは、推測を行うアルゴリズムに基づいてヒューリスティックに動作します。その結果、ガベージ コレクターはユーザーの言うことを「聞かない」ことがよくあります。

"意味?"

「Java には、 System.gc () メソッドを使用して呼び出すことができるガベージ コレクター ( GC ) オブジェクトがあります。」

System.runFinalization()を使用して、削除するオブジェクトのファイナライズ メソッドの呼び出しを強制することもできます。しかし実際のところ、Java ドキュメントによると、これではガベージ コレクションが開始されることも、finalize( ) メソッドが呼び出されます。 いつ、何を呼び出すかはガベージ コレクターが決定します

「おお! わかってよかった。」

「しかし、それだけではありません。ご存知のとおり、Java では、一部のオブジェクトが他のオブジェクトを参照します。この参照のネットワークは、オブジェクトを削除する必要があるかどうかを決定するために使用されます。」

「そして、見てください。Java には、このプロセスに影響を与えるための特別な参照があります。それらのための特別なラッパー クラスがあります。それらは次のとおりです。」

SoftReference はソフトリファレンスです。」

WeakReference は弱い参照です。」

PhantomReferenceはファントム参照です。」

「うーん...これを見ると、内部クラス、ネストされたクラス、ネストされた匿名クラス、およびローカル クラスを思い出します。名前は異なりますが、それらが何のためにあるのかはまったく明らかではありません。」

「なあ、アミーゴ、君はプログラマーになったんだ。今、君はクラス名のせいで怒っているんだ、『それらは十分な情報を提供していない、そして 1 つの名前では (!) このクラスが何をどのように行うのかを決定するのは不可能だ、と言う。なぜ"。"

「わあ、全然気づかなかった。でも、すごく明らかだよ」

「わかりました。もう十分です。SoftReferences についてお話しましょう。」

「これらの参照はキャッシュ用に特別に設計されていますが、他の目的にも使用できます。すべてプログラマーの裁量で決まります。」

「そのような参照の例は次のとおりです。」

// Create a Cat object
Cat cat = new Cat();

// Create a soft reference to a Cat object
SoftReference<Cat> catRef = new SoftReference<Cat>(cat);

// Now only the catRef soft reference points at the object
cat = null;

// Now the ordinary cat variable also references the object
cat = catRef.get();

// Clear the soft reference
catRef.clear();

「オブジェクトへの参照のみがソフトである場合、そのオブジェクトは引き続き存続し、「ソフト到達可能」と呼ばれます。」

「しかし! ソフト参照によってのみ参照されるオブジェクトは、プログラムに十分なメモリがない場合、ガベージ コレクターによって削除される可能性があります。プログラムに突然十分なメモリが不足した場合、OutOfMemoryException をスローする前に、ガベージ コレクターはすべてのオブジェクトを削除します。ソフト参照によって参照されており、プログラムへのメモリの割り当てを再試行します。」

「クライアント プログラムがサーバー プログラムにさまざまなデータを頻繁に要求するとします。サーバー プログラムは、SoftReference を使用してその一部をキャッシュできます。ソフト参照によって死から守られているオブジェクトがメモリの大部分を占有している場合、ガベージ コレクターはそれらを単に削除します。全部。美しいですね!」

「はい。私自身も気に入りました。」

「そうですね、小さな追加が 1 つあります。SoftReferenceクラスには 2 つのメソッドがあります。 get() メソッドは、SoftReferenceによって参照されるオブジェクトを返します。オブジェクトがガベージ コレクターによって削除された場合、get () メソッドは突然 null を返し始めます。」

「ユーザーは、 clear() メソッドを呼び出してSoftReference を明示的にクリアすることもできます。この場合、SoftReferenceオブジェクト内の弱いリンクは破壊されます。」

"それは今のところすべてです。"

「興味深い話をありがとう、エリー。本当にとても面白かった。」