Java ガベージ コレクションのメカニズムの詳細な説明
一見したところ、ガベージ コレクションが行うことは、その名前が示すとおり、ガベージを見つけて削除することです。実際にはまったく逆です。ガベージ コレクションは、まだ使用されているすべてのオブジェクトを追跡し、残りのオブジェクトをガベージとしてマークします。これを念頭に置いて、「ガベージ コレクション」と呼ばれるこの自動メモリ リサイクルが JVM でどのように実装されるかを詳しく見てみましょう。
手動メモリ管理
最新バージョンのガベージ コレクションを紹介する前に、メモリを明示的に手動で割り当て、解放する必要があった時代を簡単に振り返ってみましょう。メモリの解放を忘れると、そのメモリは再利用できなくなります。このメモリは占有されていますが、使用されていません。このシナリオはメモリ リークと呼ばれます。
以下は、C で書かれた手動メモリ管理の簡単な例です。
int send_request() { size_t n = read_size(); int *elements = malloc(n * sizeof(int)); if(read_elements(n, elements) < n) { // elements not freed! return -1; } // … free(elements) return 0; }
ご覧のとおり、メモリの解放は簡単に忘れてしまいます。メモリ リークは、かつては非常に一般的な問題でした。常にコードを修正することによってのみ、それらと戦うことができます。したがって、人的エラーの可能性を減らすために、未使用のメモリを自動的に解放する、より洗練された方法が必要です。この自動化されたプロセスは、ガベージ コレクション (略して GC) とも呼ばれます。
スマート ポインター
自動ガベージ コレクションの初期の実装は参照カウントです。各オブジェクトが何回参照されたかがわかり、カウンターが 0 に達すると、オブジェクトを安全にリサイクルできます。 C++ の共有ポインターは非常に有名な例です。
int send_request() { size_t n = read_size(); stared_ptr<vector<int>> elements = make_shared<vector<int>>(); if(read_elements(n, elements) < n) { return -1; } return 0; }
私たちが使用するsharedptrは、このオブジェクトが参照された回数を記録します。他の人に渡すとカウントは 1 つ増え、範囲外になると 1 つ減ります。このカウントが 0 に達すると、sharedptr は基礎となる対応するベクトルを自動的に削除します。もちろん、これは単なる例であり、一部の読者はこれが実際に起こる可能性は低いと指摘していますが、デモンストレーションとしては十分です。
自動メモリ管理
上記の C++ コードでは、メモリ管理を使用する必要があることを明示的に宣言する必要もあります。では、すべてのオブジェクトがこのメカニズムを使用するとどうなるでしょうか?これは非常に便利なので、開発者はメモリのクリーンアップについて考える必要がありません。ランタイムは、どのメモリが使用されなくなったかを自動的に認識し、解放します。つまり、自動的にゴミをリサイクルしてくれるのです。第一世代のガベージ コレクターは 1959 年に Lisp に導入され、そのテクノロジーは今日まで進化し続けています。
参照カウント
C++ の共有ポインタを使用して説明したアイデアは、すべてのオブジェクトに適用できます。 Perl、Python、PHP などの多くの言語がこの方法を使用します。これは、図で簡単に説明できます:
緑色の雲は、プログラムでまだ使用されているオブジェクトを表しています。技術的なレベルから見ると、これは実行されるメソッドのローカル変数、または静的変数に似ています。状況はプログラミング言語によって異なる可能性があるため、これは私たちの焦点ではありません。
青い円はメモリ内のオブジェクトを表しており、それらを参照しているオブジェクトの数がわかります。灰色の円内のオブジェクトは誰からも参照されなくなります。したがって、これらはガベージ オブジェクトであり、ガベージ コレクターによってクリーンアップできます。
いいですね?はい、しかし大きな欠陥があります。いくつかの孤立したリングが出現するのは簡単ですが、それらのオブジェクトはどのドメインにも属していませんが、それらは相互に参照するため、ゼロ以外の参照番号が得られます。以下に例を示します:
ご覧のとおり、赤い部分は実際にはアプリケーションで使用されなくなったガベージ オブジェクトです。参照カウントの欠陥により、メモリ リークが発生します。
この問題を解決するには、特別な「弱い」参照を使用するか、循環参照をリサイクルする特別なアルゴリズムを使用するなど、いくつかの方法があります。 Perl、Python、PHP などの前述の言語はすべて、同様の方法を使用して循環参照をリサイクルしますが、これはこの記事の範囲外です。 JVMで使われる方法を詳しく紹介していきます。
削除のマーク
まず第一に、JVM のオブジェクト到達可能性の定義をより明確にする必要があります。以前の緑色の雲ほど曖昧ではありませんが、ガベージ コレクション ルートの非常に明確かつ具体的な定義があります:
ローカル変数
アクティブ スレッド
静的フィールド
JNI 参照
その他 (後で説明します)
JVM は、マークアンド削除アルゴリズムを通じて、到達可能な (生存している) オブジェクトをすべて記録しますが、到達できないオブジェクトのメモリは再利用できます。これは 2 つのステップで構成されます:
マーキングは、到達可能なすべてのオブジェクトを走査し、これらのオブジェクトの情報をローカル メモリに記録することを指します
削除は、到達不可能なオブジェクトのメモリ アドレスが次のメモリ割り当てで使用できるようにすることを意味します。
Parallel Scavenge、Parallel Mark+Copy、CMS などの JVM のさまざまな GC アルゴリズムはすべてこのアルゴリズムの異なる実装ですが、概念的にはこれらの 2 つのステップに対応しています。
この実装で最も重要なことは、オブジェクト ループのリークがなくなることです。
欠点は、リサイクルを完了するためにアプリケーション スレッドを一時停止する必要があることです。参照が変更され続ける場合、それをカウントすることはできません。 JVM がその雑務を処理できるようにアプリケーションが一時停止される状況は、Stop The World 一時停止 (STW) とも呼ばれます。この一時停止がトリガーされる可能性は数多くありますが、おそらく最も一般的なのはガベージ コレクションです。
読んでいただきありがとうございます、皆さんのお役に立てれば幸いです、このサイトをサポートしていただきありがとうございます!
Java ガベージ コレクション メカニズムに関連するその他の記事については、PHP 中国語 Web サイトに注目してください。