1。概要
誰もが知っているように、ガベージ コレクターのタスクは「死んだ」オブジェクトをリサイクルすることです。
次に、ガベージ コレクターがヒープを再利用する前に、まず、これらのオブジェクトがまだ「生きている」かどうかを確認します。
ここには 2 つのアルゴリズムがあります。
- 参照カウントアルゴリズム
- 到達可能性分析アルゴリズム
2. 参照カウントアルゴリズム
参照カウント アルゴリズムは、オブジェクトに参照カウンターを追加することです. オブジェクトへの参照があるたびに、カウンターは 1 ずつインクリメントされ、使用されます. したがって、ガベージ コレクターはこのオブジェクトをリサイクルできます。
ガベージ コレクタは、カウントのために追加のメモリを占有する必要がありますが、その原理は単純であり、その判断効率は非常に高いです。ただし、Java 仮想マシンは参照カウント アルゴリズムを使用してメモリを管理しません。
これは、単純な参照カウントでは、オブジェクト間の循環依存の問題を解決するのが難しいためです。
たとえば、A と B の 2 つのオブジェクトがあり、A オブジェクトは B オブジェクトを参照し、B オブジェクトは A オブジェクトを参照し、他の参照はありませんが、実際にはこの 2 つのオブジェクトはもう使用できません。参照カウント アルゴリズムが使用されている場合、2 つのオブジェクトが相互に参照するため、参照カウンターは両方とも 1 であり、ガベージ コレクターはそれらを再利用できません。
3. アクセシビリティ分析アルゴリズム
到達可能性分析アルゴリズムの考え方は、一連のいわゆる“GC Roots”
ルート オブジェクトを開始ノード セットとして使用し、これらのノードから開始し、参照関係に従って下方向に検索し、検索プロセスによって移動されたパスを呼び出します。 「参照チェーン」 .
オブジェクト間に参照リンクがない場合GC Roots
、またはオブジェクトがグラフ理論から到達できない場合GC Roots
、そのオブジェクトはもはや使用できないことが証明されます。
図のobject5
とobject6
と はobject7
関係がありますが、GC Root
到達できないため、リサイクル対象と判断できます。
Java では、GC Roots
修正できるオブジェクトには次のものがあります。
- 各スレッドが呼び出すメソッドスタックで使用されるパラメータ、ローカル変数、一時変数など、仮想マシンスタック(スタックフレーム内のローカル変数テーブル)で参照されるオブジェクト。
- Java クラスの参照型静的変数など、メソッド領域のクラス静的プロパティによって参照されるオブジェクト。
- メソッド領域の参照など、メソッド領域
StringTable
の定数参照オブジェクト。 - ネイティブ メソッド stack 内の JNI (つまり、ネイティブ メソッド) によって参照されるオブジェクト。
- 基本的なデータ型に対応する Class オブジェクト、一部の例外オブジェクト (
NullPointException
など)、システム クラス ローダーなど、Java 仮想マシン内の参照。 - 同期ロック (キーワード)
synchronized
によって保持されるすべてのオブジェクト。 - Java 仮想マシンの内部状況
JMXBean
、JVMTI
に登録されているコールバック、ローカル コード キャッシュなどを反映します。
これらに加えて、ユーザーが選択したガベージ コレクターと現在回収されているメモリ領域に応じて、他のオブジェクトを「一時的に」追加して完全なガベージ コレクターを形成することもできます。GC Roots
4. 引用の分類
JDK1.2 より前では、reference
データ型に格納された値が別のメモリ ブロックの開始アドレスを表す場合、そのデータはreference
メモリ ブロックまたはオブジェクトへの参照を表すと言われていました。
JDK1.2以降、参照は強参照、弱参照、弱参照、幻参照に分けられます。
- 強い参照
- プログラム コード内の参照割り当てを参照します。たとえば
Object obj = new Object()
、強力な参照関係が存在する限り、ガベージ コレクターは参照されたオブジェクトをリサイクルしません。
- プログラム コード内の参照割り当てを参照します。たとえば
- ソフト参照
- ソフト参照は、有用ではあるが必須ではないオブジェクトを記述します
- ソフト参照に関連付けられたオブジェクトは、システムでメモリ オーバーフローの例外が発生しようとしているときにガベージ コレクションをトリガーします (メモリは存続できません)。ガベージ コレクションの後、まだメモリが不足している場合、オブジェクトが強く参照されていない場合、これらのオブジェクトはリサイクルされます。それが2回目のリサイクルです。
- JDK1.2以降は
SoftReference
ソフトリファレンスを提供 - キューと連携してソフト参照自体を解放できます
- 弱引用
- 弱い参照は、重要でないオブジェクトを記述するためにも使用されます
- 弱い参照に関連付けられたオブジェクトは、次のガベージ コレクションが発生するまで存続できます。十分なメモリがあるかどうかに関係なく、弱い参照にのみ関連付けられているオブジェクトはリサイクルされます。
- JDK1.2以降、
WeakReference
弱い参照が提供されます - キューと連携して弱い参照自体を解放できます
- ファントムリファレンス
- 主に
ByteBuffer
一緒に使用され、参照されたオブジェクトがリサイクルされると、ファントム参照がキューに入れられ、スレッドはReference Handler
ファントム参照関連のメソッドを呼び出して直接メモリを解放します。詳細は【JVM】ダイレクトメモリの詳細解説を参照 - JDK1.2以降、
PhantomReference
仮想参照を実装するためのクラスが提供されています
- 主に
- ファイナライザ リファレンス
- 手動コーディングの必要はありませんが、参照キューで内部的に使用されます. ガベージ コレクション中にファイナライザー参照がキューに入れられ (参照されるオブジェクトは一時的に再利用されません)、
Finalizer
スレッドはfinalize
されたオブジェクトは、2 番目の GC でのみ復元できます。
- 手動コーディングの必要はありませんが、参照キューで内部的に使用されます. ガベージ コレクション中にファイナライザー参照がキューに入れられ (参照されるオブジェクトは一時的に再利用されません)、
4.1 ソフト参照の適用
前提、VM Options
追加――-Xmx20m -XX:+PrintGCDetails -verbose:gc
-XX:+PrintGCDetails -verbose:gc
ごみ収集情報を印刷できます
private static final int _4MB = 10 * 1024 * 1024;
public static void main(String[] args) throws IOException {
List<byte[]> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
list.add(new byte[_4MB]);
}
System.in.read();
}
上記のコードを実行すると、明らかにヒープ メモリ領域が不足するため、スローされます。OutOfMemoryError
ただし、ソフト参照に切り替えれば、ヒープ領域が不足することはありません。
public static void soft() {
// list --> SoftReference --> byte[]
List<SoftReference<byte[]>> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
System.out.println("循环结束:" + list.size());
for (SoftReference<byte[]> ref : list) {
System.out.println(ref.get());
}
}
コンソールを見ると、メモリ不足が原因でガベージ コレクションがトリガーされた場合、ソフト参照に関連付けられたオブジェクトは最初はリサイクルされず、2 回目のガベージ コレクションとメモリのときにのみ収集されることがわかります。参照されたオブジェクトはリサイクルされます。
キューを使用してソフト参照を削除できます
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args) {
List<SoftReference<byte[]>> list = new ArrayList<>();
// 引用队列
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
for (int i = 0; i < 5; i++) {
// 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
// 从队列中获取无用的 软引用对象,并移除
Reference<? extends byte[]> poll = queue.poll();
while( poll != null) {
list.remove(poll);
poll = queue.poll();
}
System.out.println("===========================");
for (SoftReference<byte[]> reference : list) {
System.out.println(reference.get());
}
}