1。Javaメモリモデル
プログラムを実行すると、Java仮想マシンは、管理するメモリをいくつかのデータ領域に分割します。これらのデータ領域の分布を以下の図に示します。
プログラムカウンター:現在実行されているバイトコードを指す小さなメモリ領域。スレッドがJavaメソッドを実行している場合、このカウンターは、実行中の仮想マシンバイトコード命令のアドレスを記録します。ネイティブメソッドが実行された場合、計算値は空です。
Java Virtual Machine Stack:スレッドはプライベートであり、ライフサイクルはスレッドと一致しています。各メソッドが実行されると、ローカル変数テーブル、オペランドスタック、ダイナミックリンク、メソッドエグジなどの情報を保存するためにスタックフレームが作成されます。
ローカルメソッドスタック:機能は仮想マシンスタックに似ていますが、仮想マシンスタックは仮想マシンのJavaメソッドサービスを実行し、ローカルメソッドスタックは使用されるネイティブメソッドを提供します。
Java Heap:すべてのスレッドで共有される仮想マシン管理メモリの最大の部分であり、この領域はオブジェクトインスタンスを保存するために使用され、ほとんどすべてのオブジェクトがこの領域で割り当てられています。 Javaヒープは、メモリリサイクルの主要な領域です。メモリリサイクルの観点から見ると、現在のコレクターのほとんどは世代のコレクションアルゴリズムを使用しているため、Javaヒープも次のように分割できます。新世代と旧世代。少し細分されている場合は、エデン空間、生存空間から生存空間などに分けることができます。Java仮想マシンの仕様によると、Javaヒープは、論理的に連続している限り、物理的に不連続な空間にあります。
メソッド領域:Javaと同様に、さまざまなスレッドで共有され、常に仮想マシンによってロードされたクラス情報などのデータを保存するために使用されます。
ランタイム定数プール、ランタイム定数プールはメソッド領域の一部です。クラスバージョン、フィールド、メソッド、インターフェイス、その他の説明情報に加えて、クラスファイルには一定のプールもあります。これは、コンピレーション期間中に生成されるさまざまなリテラルおよびシンボリック参照を保存するために使用されます。ランタイム中、新しい定数を一定のプールに配置できます。最も一般的に使用されるのは、文字列クラスのインターン()メソッドです。文字列インスタンスがインターンを呼び出すと、Javaは定数プールに同じユニコード文字列定数があるかどうかを見つけます。ある場合、参照を返します。そうでない場合は、インスタンス文字列に等しいユニコードを追加し、その参照を返します。
2。ごみのオブジェクトを決定する方法
Javaヒープに保存されているオブジェクトインスタンスがいくつかあります。ゴミコレクターがヒープをリサイクルする前に、最初にどのオブジェクトがまだ「生きている」か、どちらが「死んでいる」か、つまり、いかなる手段でも使用されないオブジェクトを決定する必要があります。
引用カウント
引用カウント方法は、簡単に実装して効率的になり、ほとんどの場合、優れたアルゴリズムです。原則は次のとおりです。オブジェクトに参照カウンターを追加します。オブジェクトを参照する場所がある場合はいつでも、カウンターは1増加します。参照が故障すると、カウンターが1に削減されます。カウンター値が0の場合、オブジェクトが使用されなくなったことを意味します。参照カウント方法は、オブジェクト間の相互参照の問題を解決することが困難であり、主流のJava仮想マシンは参照カウント方法を使用してメモリを管理しないことに注意する必要があります。
アクセシビリティ分析アルゴリズム
このアルゴリズムの基本的なアイデアは、これらのノードから始まる「GC Roots」と呼ばれる一連のオブジェクトを開始点として下方に検索することです。検索されたパスは、参照チェーンと呼ばれます。オブジェクトが参照チェーンなしでGCルーツに接続されていない場合(グラフ理論の言葉では、GC Rootsからこのオブジェクトへの到達不可能なものです)、このオブジェクトが利用できないことが証明されています。図に示すように、オブジェクト5、オブジェクト6、およびオブジェクト7は互いに関連していますが、GC Rootsに到達できないため、リサイクル可能なオブジェクトであると判断されます。
Java言語では、GCルーツとして使用できる次のオブジェクトには次のものがあります。
仮想マシンスタックで参照されているオブジェクト(スタックフレームのローカル変数テーブル)。
メソッド領域のクラスの静的属性によって参照されるオブジェクト。
メソッド領域の定数によって参照されるオブジェクト。
ローカルメソッドスタックでJNI(つまり、一般的なネイティブ方法)が参照するオブジェクト。
さて、問題は、アクセシビリティ分析アルゴリズムにはオブジェクト間の循環参照の問題があるのでしょうか?答えは「はい」です。つまり、オブジェクト間の循環参照の問題はありません。 GCルートは、オブジェクトグラフの外側の特別に定義された「開始点」であり、オブジェクトグラフ内のオブジェクトで参照することはできません。
死ぬか死なないか
アクセシビリティ分析アルゴリズムの到達不可能なオブジェクトでさえ、「死ぬ必要があります」。この時点で、彼らは一時的に「保護観察」段階にいます。オブジェクトを真に宣言するには、少なくとも2つのマーキングプロセスを通過する必要があります。オブジェクトが、アクセシビリティ分析を実行した後にGCルーツに接続された参照チェーンがないことを見つけた場合、初めてマークされ、フィルタリングされます。フィルタリング条件は、このオブジェクトがfinapze()メソッドを実行する必要があるかどうかです。オブジェクトがfinapze()メソッドまたはfinapze()メソッドが仮想マシンによって呼び出されない場合、仮想マシンは両方のケースを「実行する必要はない」と見なします。このプログラムでは、Finapze()を上書きして「スリリングな」自己告げプロセスを作成できますが、これは1つのチャンスにすぎません。
/** *このコードは2つのポイントを示しています。 * 1。オブジェクトはGCの場合、自分自身を保存できます。 * 2。オブジェクトのfinapze()メソッドは最大でシステムによって自動的に1回だけ呼び出されるため、自己救済の可能性は1つしかありません */ pubpc class finapzeescapegc {pubpc static finapzeescapegc save_hook = null; pubpc void isapve(){system.out.println( "はい、私はまだapve :)"); } @Override Protected void finapze()Throws throwable {super.finapze(); system.out.println( "finapze mehtod exected!"); finapzeescapegc.save_hook = this; } pubpc static void main(string [] args)throws throwable {save_hook = new finapzeescapegc(); //オブジェクトは、初めてsave_hook = null; System.gc(); // finapzeメソッドの優先度が低いため、0.5秒間一時停止して、スレッド(500)を待ちます。 if(save_hook!= null){save_hook.isapve(); } else {system.out.println( "いいえ、私は死んでいます:(");} //次のコードは上記とまったく同じですが、今回は自己レスキューが失敗しました。Save_hook= null; system.gc(); save_hook.isapve();実行中の結果は次のとおりです。
finapze mehtodが実行されました!はい、私はまだapveです:)いいえ、私は死んでいます:(
引用について話しましょう
参照カウントアルゴリズムを介してオブジェクトの参照の数を判断するか、オブジェクトの参照チェーンがアクセシビリティ分析アルゴリズムを介して到達可能かどうかを判断して、オブジェクトの生存が「参照」に関連しているかどうかを判断するかどうか。 JDK 1.2の前に、Javaの参照の定義は非常に伝統的でした。参照型データに保存されている値が別のメモリの開始アドレスを表している場合、このメモリは参照を表していると言われています。 JDK 1.2の後、Javaは参照の概念を拡張し、参照を4つのタイプに分割しました:強い参照、ソフトリファレンス、弱い参照、およびPhantomリファレンス。これらの4種類の参照の強度は、徐々に弱くなりました。
•強力な引用とは、「Object Obj = new object()」など、プログラムコードで一般的な参照を指します。強い引用がまだ存在する限り、ゴミコレクターは参照されるオブジェクトを決してリサイクルしません。
•ソフト参照は、有用ではないが必要ではないオブジェクトを記述するために使用されます。ソフト参照関連オブジェクトの場合、これらのオブジェクトは、システムがメモリオーバーフロー例外を持つ前に、2回目のリサイクルのリサイクルスコープにリストされます。このリサイクルに十分なメモリがない場合、メモリオーバーフロー例外がスローされます。 JDK 1.2の後、ソフト参照を実装するためにSoftReferenceクラスが提供されます。
•弱い参照は、非必須オブジェクトを記述するためにも使用されますが、その強度はソフト参照よりも弱いです。弱い参照に関連付けられたオブジェクトは、次のゴミ収集が発生するまで生き残ることができます。ガベージコレクターが機能する場合、現在のメモリで十分かどうかに関係なく、弱い参照にのみ関連するオブジェクトが収集されます。 JDK 1.2の後、弱い参照を実装するためにWeakReferenceクラスが提供されます。
•void quotは、ゴースト引用またはファントムの引用とも呼ばれ、最も弱い引用関係です。オブジェクトが仮想参照を持っているかどうかは、生存時間にまったく影響を与えません。また、仮想参照を介してオブジェクトインスタンスを取得することもできません。オブジェクトの仮想リファレンス関連を設定する唯一の目的は、オブジェクトがコレクターによってリサイクルされたときにシステム通知を受信することです。 JDK 1.2の後、仮想参照を実装するためにPhantomreferenceクラスが提供されます。
ソフトリファレンス使用の例:
パッケージjvm; Import java.lang.ref.softreference; class node {pubpc string msg = "";} pubpc class hello {pubpc static void main(string [] args){node node1 = new node(); //強い参照node1.msg = "node1"; softreference <node> node2 = new softreference <node>(node1); //ソフトリファレンスnode2.get()。msg= "node2"; system.out.println(node1.msg); system.out.println(node2.get()。msg);}}}出力の結果は次のとおりです。
node2node2
3.典型的なガベージコレクションアルゴリズム
1.マークスイープ(マーククリア)アルゴリズム
これは、最も基本的なごみ収集アルゴリズムです。それが最も基本的であると言われている理由は、実装するのが最も簡単で最も単純なアイデアであるためです。マーククリアリングアルゴリズムは、マーキングステージとクリアリング段階の2つの段階に分かれています。マーキング段階のタスクは、リサイクルする必要があるすべてのオブジェクトをマークすることであり、クリアリングステージは、マークされたオブジェクトが占めるスペースをリサイクルすることです。特定のプロセスを以下の図に示します。
図から、マーククリアリングアルゴリズムの実装が簡単であることが簡単にわかりますが、メモリフラグメントを簡単に生成するのは深刻な問題があります。断片が多すぎると、後続のプロセスで大きなオブジェクトにスペースを割り当て、新しいガベージコレクションアクションを事前にトリガーするときに、十分なスペースを見つけることができなくなる可能性があります。
2。コピーアルゴリズム
Mark-Sweepアルゴリズムの欠点を解決するために、コピーアルゴリズムが提案されました。利用可能なメモリを容量ごとに2つの等しいサイズに分割し、一度に1つのピースのみを使用します。このメモリの一部を使用したら、まだ生きているオブジェクトを別のピースにコピーしてから、使用済みのメモリスペースを一度にクリーンアップして、メモリの断片化の問題が発生しないようにします。特定のプロセスを以下の図に示します。
このアルゴリズムは実装が簡単で、実行が効率的であり、メモリの断片化を簡単に生成するのは簡単ではありませんが、使用できるメモリが元のメモリの半分に縮小されるため、メモリスペースを使用するのは費用がかかります。
明らかに、コピーアルゴリズムの効率は、生き残ったオブジェクトの数と多くの関係があります。多くの生き残ったオブジェクトがある場合、コピーアルゴリズムの効率が大幅に削減されます。
3。Mark-Compact(Mark-Collation)アルゴリズム
コピーアルゴリズムの欠点を解決し、メモリスペースを最大限に活用するために、Mark-Compactアルゴリズムが提案されています。アルゴリズムはMark-Sweepと同じですが、マークを完成させた後、リサイクル可能なオブジェクトを直接クリーンアップするのではなく、すべての生きたオブジェクトを一端に移動し、エンド境界の外側のメモリをクリーンアップします。特定のプロセスを以下の図に示します。
4。世代コレクションアルゴリズム
現在、ジェネレーションコレクションアルゴリズムは、ほとんどのJVMガベージコレクターで使用されています。その核となるアイデアは、オブジェクトの生存のライフサイクルに応じて、記憶をいくつかの異なる領域に分割することです。一般的に言えば、ヒープ領域は古い世代と若い世代に分かれています。古い世代の特徴は、ごみが収集されるたびにリサイクルする必要があるのは少数のオブジェクトのみが、新世代の特徴は、ガベージを収集するたびに多数のオブジェクトをリサイクルする必要があることです。次に、さまざまな世代の特性に従って、最も適切なコレクションアルゴリズムを採用できます。
現在、ほとんどのゴミコレクターは新世代のコピーアルゴリズムを採用しています。新世代では、ほとんどのオブジェクトを毎回リサイクルする必要があります。つまり、コピーする必要がある操作の数は少ないですが、実際には、新世代の空間は1:1の比率に応じて分割されません。一般的に言えば、新世代はより大きなエデン空間と2つの小さな生存空間(通常8:1:1)に分割されます。エデンスペースとサバイバースペースの1つが使用されるたびに、リサイクル時にエデンとサバイバーでまだ生存しているオブジェクトが別のサバイバースペースにコピーされ、エデンと使用されたばかりの生存空間が掃除されます。
老年は毎回少数のオブジェクトのみがリサイクルされることであるため、マークコンパクトアルゴリズムが一般的に使用されます。
Javaメモリモデルとゴミコレクションの上記の簡単な分析は、私があなたと共有するすべてのコンテンツです。参照を提供できることを願っています。wulin.comをもっとサポートできることを願っています。