C がすごいと言われるのは、C はプログラムがメモリ内でどのようなものかを正確に知っているからです。 Java に関しては、オペレーティング システムに関する知識が豊富である必要はなく、メモリの問題に常に注意を払う必要もありませんが、原理を理解する必要がないという意味ではありません。その裏では(実際、会社のCooOさんに嘲笑されていました)。 Java が簡単に始められる理由は、最も困難な問題が先人によって解決されているためであり、これはすべて Java 仮想マシンのおかげであり、実際には独自の命令セットを持つ抽象コンピューターです。言語と独自のメモリ管理。このシリーズではその正体を一つ一つ明らかにしていきます。
この記事は、Java HotSpot™ 仮想マシン、JDK 1.8 に基づいており、以下について説明します:
JVM の内部構造
JVM メモリ管理
JVM メモリ モデル
図 1 JVM の内部構造
1. JVM の内部構造
プログラムの実行プロセスは次のようになります。C 言語を例にとると、ソース コードはまず実行可能ファイルにコンパイルされ、バイナリ形式でディスクに保存されます。ディスクをメモリにロードし、プロセッサがターゲット プログラムで機械語命令の実行を開始します。対照的に、Java は最初にバイトコード ファイルにコンパイルされますが、これはプラットフォームとは何の関係もありません。JVM は ClassLoader を通じてメモリにロードされ、その後、マシン命令を実行します。バイトコードと JVM により、Java はプラットフォームからの独立性を実現します。 JVM はプロセスとして考えることもできます。起動時にメモリの一部を適用し、そのメモリをさまざまな機能に従って次のさまざまな領域に分割します:
(1) ヒープ
ヒープ、非常に重要な領域です。基本的にすべてのオブジェクト インスタンスがここに割り当てられ、ほとんどのガベージ コレクションがここで行われます。メモリのこの部分は、ガベージ コレクタ (自動メモリ管理ツール) を使用して JVM によって管理され、オブジェクトにメモリを割り当て、空きメモリを解放します。 Java ヒープのサイズは、固定か動的拡張かを決定するパラメータを使用して制御できます。
(2) JVM スタック
スタックはスレッドと密接に関係しており、スレッドとともに存続し、スレッドとともに消滅します。 HotSpot の Java スタックとローカル メソッド スタックは 1 つに結合され、両方ともローカル メモリ空間に割り当てられます。メモリのこの部分は JVM によって意図的に管理される必要はありません。 JVM スタックは主にスタック フレームを保存するために使用され、メソッドが呼び出されるときにスタック フレームが作成され、スタックの観点からはプッシュとポップの 2 つの操作があります。
スタックフレームは、ローカル変数、オペランドスタック、現在のクラスのランタイム定数プールへの参照を保存するために使用されるデータ構造です。ローカル変数配列: メソッドで定義された基本型変数を保存するために使用されます。添え字は 0 から始まります。JVM は、インスタンス メソッドが呼び出されるときに、ローカル変数テーブルを使用して、この参照を格納します。現在のオブジェクト; オペランド スタック: 操作を実行し、メソッドを呼び出すためのパラメータを準備し、メソッドの結果を返すために使用されます。 ダイナミック リンク: 参照オブジェクトの実行時定数プール。
(3) PC Register
プログラムカウンター、スレッドプライベート、主な機能は命令アドレスの保存、フェッチ、デコード、実行です。各スレッドは、固有のスタックおよび PC レジスタに関連付けられています。
(4) Metaspace
JDK8 以前の HotSpot VM である Metaspace は、メソッド領域または永続世代と呼ばれ、各スレッドで共有される定数プール、フィールド、メソッド、などのクラスの構造情報を格納します。等ヒープとは関係なく、ローカル メモリに保存されます。
(5)ネイティブメソッドスタック
JVMスタックはJavaメソッド用に用意されており、ローカルメソッドスタックは仮想マシンがローカルメソッドを呼び出す役割を果たします。
2. JVMメモリ管理
Javaではメモリを直接操作することができず、メモリの適用と解放は仮想マシンによって処理されます。
2.1 ガベージコレクション自動メモリ管理
自動メモリ管理(以下、GC)の責任:
メモリを割り当てる
参照オブジェクトがメモリ内に残るようにする
到達不能な参照オブジェクトのメモリをリサイクルする
GCはほとんどのことを解決する問題の概要 メモリ割り当ての問題自体も、特定のリソースを消費します。ガベージ コレクションは、ヒープがいっぱいになるか、そのコンポーネントの 1 つがしきい値に達するとトリガーされます。ガベージ コレクションでは、主にリサイクルの頻度と時間が考慮されます。たとえば、ヒープが小さい場合、リサイクルの回数は多くなりますが、ヒープが大きい場合、リサイクルの回数は少なくなります。リサイクルに時間がかかる; マルチスレッド プログラムでのガベージ コレクションの問題。
ガベージコレクション戦略:
(1) シリアル vs パラレル
シリアルでは、同時に 1 つのガベージ コレクション スレッドのみが動作できますが、パラレルでは、マルチ CPU システムでは、複数のガベージ コレクション スレッドが同時に動作できます。
(2) Concurrent vs Stop-the-world (Concurrent vs Stop-the-world)
Stop-the-world ガベージコレクタ、リサイクル期間中、アプリケーションは完全に作業を停止し、ヒープは凍結されている場合、オブジェクトの状態は不変です。同時に、1 つ以上のガベージ コレクション タスクがアプリケーションと同時に実行され、短いストップ ザ ワールドが発生する可能性があり、オブジェクトの状態はコレクション中に変更される可能性があります。 。
(3) コピー
リサイクルする場合、残りのオブジェクトを残りの半分の領域にコピーし、その後のメモリの割り当てが容易になりますが、メモリ使用率は比較的低くなります。 。
(4) 圧縮と非圧縮
マークのクリア、リサイクル可能なオブジェクトのマーク、およびそれらの均一なリサイクル メモリ圧縮を行わないと、大きなオブジェクトを割り当てるときに大量のメモリの断片が生成され、連続したオブジェクトを見つけることが不可能になる可能性があります。メモリ; マーキングとソート。マーキング後、最初にメモリが圧縮されてソートされ、残ったすべてのオブジェクトがリサイクルのためにまとめられます。
(5) 世代別収集
ヒープを新しい世代と古い世代のいくつかのエリアに分割します。異なるエリアでは、上記の異なるリサイクル方法が使用されます。
2.2 HotSpot の世代別コレクション
HotSpot では、メモリが新世代と旧世代に分割され、新世代は Eden と同じサイズの 2 つの Survivor スペースに分割され、ほとんどのオブジェクトが Eden に割り当てられます。大きなオブジェクトは Eden に割り当てられ、古い世代に直接割り当てられる場合があります。
ヒープの構造は次のとおりです:
図 2 ヒープ