前書き
segmentfault に関する質問を見ました: Java には完全な GC メカニズムがあるため、Java でメモリ リークは発生しますか? メモリ リークのケースを教えてください。この質問ビューは、この質問に対する完全な答えを提供します。
ガベージコレクションメカニズムの紹介
プログラムの実行中、オブジェクトが作成されるたびに、オブジェクトデータを保存するために一定量のメモリが割り当てられます。メモリを割り当て続けると、プログラムは遅かれ早かれメモリ不足の問題に直面することになります。したがって、どの言語でも、期限切れのオブジェクトのメモリを解放してメモリを再利用できるようにするメモリ リサイクル メカニズムが存在します。
メモリ再利用メカニズムは、実装の役割の違いに応じて 2 つのタイプに分類できます。1 つはプログラマが手動でメモリを解放するもの (C 言語など)、もう 1 つは言語に組み込まれたメモリ再利用メカニズムです。この記事で紹介する Java ガベージの仕組み。
Java のガベージ コレクション メカニズム
プログラムのランタイム環境では、Java 仮想マシンはシステムレベルのガベージ コレクション (GC、カーベッジ コレクション) スレッドを提供します。このスレッドは、参照を失ったオブジェクトによって占有されていたメモリをリサイクルする役割を果たします。 GC を理解するための前提条件は、ガベージ コレクションに関連するいくつかの概念を理解することです。これらの概念を以下に 1 つずつ紹介します。
jvm ヒープ領域内のオブジェクトのステータス
Java オブジェクトのインスタンスは、GC スレッドの場合、これらのオブジェクトには 3 つの状態があります。
1. 到達可能な状態: プログラム内に変数参照がある場合、このオブジェクトは到達可能な状態にあります。
2. 復活可能な状態: プログラム内の変数がこのオブジェクトを参照していない場合、オブジェクトはタッチ可能な状態から復活可能な状態に変わります。 CG スレッドは、特定の時点でこのオブジェクトのファイナライズ メソッドを呼び出す準備をします (ファイナライズ メソッドはサブオブジェクトを継承またはオーバーライドします)。ファイナライズ メソッド内のコードはオブジェクトをタッチ可能な状態に変換することができます。そうでない場合、オブジェクトはタッチ可能状態になります。アンタッチャブルな状態に変わった。
3. アンタッチ不可状態: オブジェクトがアンタッチ不可状態にある場合にのみ、GC スレッドはこのオブジェクトのメモリを再利用できます。
オブジェクトを正しく解放するために、GC はオブジェクトの適用、参照、参照中、割り当てなどを含む各オブジェクトの実行状態を監視する必要があります。上記の状態は GC によって認識されます。
上記の通り、GCスレッドはあるタイミングで復活可能な状態オブジェクトのfinalizeメソッドを実行しますが、いつ実行されるのでしょうか?異なる JVM 実装者は異なるアルゴリズムを使用して GC を管理する可能性があるため、開発者は、GC スレッドによるさまざまな操作 (オブジェクトのステータスの検出、オブジェクトのメモリの解放、オブジェクトのファイナライズ メソッドの呼び出しなど) のタイミングをいつでも予測できません。 System.gc() および Runtime.gc() 関数を使用して、GC スレッドにガベージ コレクション操作をできるだけ早く実行するように通知できますが、GC スレッドが対応するリサイクル操作をすぐに実行するという保証はありません。
メモリ リーク
メモリ リークとは、不適切な設計により使用されなくなったメモリをプログラムが解放できず、その結果リソースが無駄になることを指します。 GC は、参照を失ったオブジェクトによって占有されていたメモリを自動的にクリーンアップします。ただし、プログラミング エラーにより一部のオブジェクトが常に参照されると、メモリ リークが発生します。
以下の例のように。スタックは、プッシュとポップの 2 つの操作を持つ配列を使用して実装されます。
import com.sun.javafx.collections.ElementObservableListDecorator; import com.sun.swing.internal.plaf.metal.resources.metal_sv; import java.beans.ExceptionListener; import java.util.EmptyStackException; /** * Created by peng on 14-9-21. */ public class MyStack { private Object[] elements; private int Increment = 10; private int size = 0; public MyStack(int size) { elements = new Object[size]; } //入栈 public void push(Object o) { capacity(); elements[size++] = o; } //出栈 public Object pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; } //增加栈的容量 private void capacity() { if (elements.length != size) return; Object[] newArray = new Object[elements.length + Increment]; System.arraycopy(elements, 0, newArray, 0, size); } public static void main(String[] args) { MyStack stack = new MyStack(100); for (int i = 0; i < 100; i++) stack.push(new Integer(i)); for (int i = 0; i < 100; i++) { System.out.println(stack.pop().toString()); } } }
このプログラムは利用可能であり、一般的なプッシュおよびポップ操作をサポートしています。ただし、うまく処理されていない問題があります。つまり、スタックをポップするときに、配列内のポップされた要素への参照が解放されないため、プログラムはこのオブジェクトへの参照を保持します (このオブジェクトは参照されます)。配列によって) GC は常にこのオブジェクトが到達可能であると認識し、ましてやそのメモリを解放します。これはメモリ リークの典型的なケースです。これに対応して、修正されたコードは次のとおりです。
//出栈 public Object pop() { if (size == 0) throw new EmptyStackException(); Object o = elements[--size]; elements[size] = null; return o; }
Java ガベージ コレクションのメカニズムとメモリ リークについて詳しく説明した上記の記事は、すべて編集者が共有した内容であり、参考にしていただければ幸いです。皆さんが PHP 中国語 Web サイトをサポートしてくれることを願っています。
Java ガベージ コレクション メカニズムとメモリ リーク関連の記事をさらに詳しく理解するには、PHP 中国語 Web サイトに注目してください。