JVMとはJVM は、ユーザー モードで実行され、アプリケーションを通じてクロスプラットフォーム Java コードを実装する Java 仮想マシンです。これはプラットフォームに依存せず、実際には「一度コンパイルすればどこでも実行できる」ものです。 1. ミクロな視点で見ると、コンパイルされるのはバイトコードです。どのプラットフォームでも使用でき、そのプラットフォームの JDK があれば実行できます。バイトコードは人のようなもので、プラットフォームは国のようなもので、JDK はその国の言語のようなものです。この人(バイトコード)がこの国の言語(JDK)を持っている限り、この国(プラットフォーム)で生活することができます。 2. JDK は、Java ランタイム環境、一連の Java ツール、Java ベースのクラス ライブラリ (rt.jar) を含む、Java 全体の中核です。 3. Java 仮想マシン (JVM) は、さまざまな方法 (ソフトウェアまたはハードウェア) で実装できるコンピュータ デバイスの仕様です。仮想マシンの命令セットをコンパイルすることは、マイクロプロセッサの命令セットをコンパイルすることと非常に似ています。 Java 仮想マシンは、バイトコード命令セット、レジスタ セット、スタック、ガベージ コレクション ヒープ、およびストレージ メソッド領域で構成されます。 4. Java は「Java バイトコード」にコンパイルされ、仮想マシンによって解釈され実行されます。 C と C++ はバイナリにコンパイルされ、オペレーティング システムによって直接実行されます。 5. いわゆる「一度コンパイルしてどこでも実行」とは、1 か所でコンパイルするだけで、他のすべてのプラットフォームで実行できることを意味します。 6. プラットフォームに依存しないということは、JAVA が独自の JVM 上でのみ実行され、他の基盤となるクラスに依存する必要がないため、オペレーティング システムとは関係がないことを意味します。プラットフォームとはオペレーティング システムを指します。 メモリ構造図クラスファイルクラス ファイル パスは、C や C++ などの言語が従う伝統を打ち破ります。これらの従来の言語で記述されたプログラムは通常、最初にコンパイルされ、次に特定のハードウェア プラットフォームとオペレーティング システムを具体的にサポートする個別のバイナリ ファイルにリンクされます。多くの場合、あるプラットフォームで動作するバイナリ実行ファイルは、他のプラットフォームでは動作しません。 Java クラス ファイルは、Java 仮想マシンをサポートする任意のハードウェア プラットフォームおよびオペレーティング システムで実行できるバイナリ ファイルです。 実行プロセス実装プロセスの概要C++ プログラムをコンパイルしてリンクすると、バイナリ ファイルにはターゲット プロセッサのマシン言語が含まれているため、結果として得られる実行可能バイナリ ファイルは、指定されたハードウェア プラットフォームとオペレーティング システムでのみ実行できます。 Java コンパイラは、Java ソース ファイルの命令を、Java 仮想マシンの「マシン言語」であるバイトコードに変換します。 通常のプログラムとは異なり、Java プログラム (クラス ファイル) はローカル実行可能プログラムではありません。 Java プログラムを実行する場合、まず JVM (Java 仮想マシン) が実行され、次に Java クラスが JVM にロードされて実行されます。 Java クラスのロードを担当する部分はクラスローダーと呼ばれます。 JVM のクラスローダーJVM 自体には、Bootstrap ClassLoader と呼ばれる ClassLoader が含まれています。 JVM と同様に、BootstrapClassLoader はネイティブ コードで実装され、コア JavaClass (つまり、java.* で始まるすべてのクラス) をロードする役割を担います。 さらに、JVM は 2 つの ClassLoader も提供します。どちらも Java で記述され、BootstrapClassLoader によってロードされます。 Extension ClassLoader は拡張 Java クラス (たとえば、javax.* で始まるすべてのクラスや JRE の ext ディレクトリに格納されているクラス) をロードする役割を担い、ApplicationClassLoader はアプリケーション独自のクラスをロードする役割を担います。 プログラムを実行すると、JVM が起動し、ブートストラップクラスローダーが実行されます。 ClassLoader は Java コア API をロードし (この時点で ExtClassLoader と AppClassLoader もロードされます)、次に ExtClassLoader を呼び出して拡張 API をロードし、最後に AppClassLoader が CLASSPATH ディレクトリで定義されたクラスをロードします。これはプログラムの最も基本的な読み込みプロセスです。 最初のクラス ファイルは javac によってバイトコードにコンパイルされます。バイトコードの後には、クラス ローダーと呼ばれる ClassLoader があります。これは、java.class ファイルには、ロード、接続、初期化など、物理ファイルからメモリ構造までの JVM 内で実行されるロード プロセスが必要であるためです。 Linux アプリケーションにはプロセス アドレス空間があります。プロセス アドレス空間の説明は次のとおりです。 Linux は仮想メモリ管理テクノロジを使用します。各プロセスには、3G サイズの独立したプロセス アドレス空間があります。このアドレス空間はユーザー空間です。各プロセスのユーザー空間は完全に独立しており、互いに無関係です。プロセスがカーネル空間にアクセスする方法: システム コールと割り込み。 プロセスの作成やその他のプロセス関連の操作には、プロセスにメモリを割り当てる必要があります。このとき、プロセスが申請して取得するのは物理アドレスではなく、仮想アドレスのみです。 実際の物理メモリは、プロセスが実際に新しく取得した仮想アドレスにアクセスし、実際のページ フレームを割り当てるプログラムに入ったときにのみ、「ページ要求メカニズム」によって「ページ不足」例外を生成します。この例外は、仮想メモリ メカニズムの存在に対する基本的な保証です。カーネルにプロセスに物理ページを割り当て、対応するページ テーブルを作成するように指示します。そうして初めて、仮想アドレスは実際に物理アドレスにマッピングされます。 Linux オペレーティング システムは仮想メモリ テクノロジを使用し、すべてのプロセスが仮想的にメモリを共有します。プロセス アドレス空間は各プロセス内の線形アドレス領域で構成され、より重要な機能は、カーネルがプロセスにこの空間内のアドレスの使用を許可することです。通常、各プロセスには固有のアドレス空間があり、プロセス アドレス空間は互いに独立しています。ただし、プロセスはアドレス空間を共有することも選択でき、そのようなプロセスはスレッドと呼ばれます。 基本的に、すべての Linux アプリケーションはスタックとヒープを使用してこのルールに従います。 JVM もこのルールに従いますが、いくつかの改善が加えられています。 クラス ファイルはクラス ローダーを通じてメモリ空間にロードされます。ロード後は、バイトコードだけになります。次にそれを実行する必要があります。どうやって実行するのですか?実行領域は、図のクラス ローダー サブシステムの下にあります。 メモリ空間には以下が含まれます。 1. メソッド領域: ロードされたクラスの情報はメソッド領域のメモリに保存されます。仮想マシンが特定の型をロードする場合、クラス ローダーを使用して対応するクラス ファイルを見つけ、クラス ファイルの内容を読み取り、仮想マシンに転送します。 2.ヒープ: Java 仮想インスタンスにはヒープ スペースが 1 つだけあります。 3. Java スタック (Java スタック): 仮想マシンは、スタック上で直接、フレーム単位でのプッシュまたはポップの 2 つの操作のみを実行します。 Javaスタックにはコアデータ、先入後出がある 4. Nativemethodstack(ローカルメソッドスタック):文字通り、システムのローカルメソッドを呼び出します。ローカルメソッドは通常、最下層にカプセル化され、直接呼び出されます。 5. アドレス、ここではポインタの概念があり、たとえば変数からオブジェクトを参照する方法がアドレスです。 6. カウンター: バイトコードを解析するときに位置を記憶するために主に使用され、マークとして理解できます。 7. 実行エンジン: データとバイトコードがビジネス処理を実行し、最終的に目的の結果を達成します。 8. ローカルメソッドインターフェース: 基本的には、IOネットワークなどの基盤システムがオペレーティングシステム自体を呼び出す 9. ローカルメソッドライブラリ: 互換性とクロスプラットフォームのために、異なるライブラリと互換性のあるプラットフォームがあります。 追加データ情報はネイティブメソッドインターフェースとネイティブメソッドライブラリを参照します ジャムJava メモリ モデルスレッドセーフティという用語を聞いたことがあるかもしれません。高い同時実行性を実現する場合、スレッドの安全性の問題が発生します。 Java でスレッド セーフティの問題が発生するのはなぜですか? JMM が存在するため、メモリは 2 つの領域 (メイン メモリと作業メモリ) に分割されます。作業メモリは各 Java スタック専用です。 高速に実行する必要があるため、メインメモリ内のデータをローカルメモリに格納してから計算を実行し、計算が完了したらデータをエコーバックする必要があります。 JMMにはメインメモリとスタックメモリの2つの領域があり、 複数の Java スレッドと複数のスタックが存在する場合があります。操作を同時に実行するには、3 つのスレッドが必要になります。メインメモリの初期値は x=0 です。 x=0 を自分のメモリにロードする必要があります。これは、 コピー、初期値、3つのスタックはすべてx=0です 計算をする必要があります
期待値は x=0 です。単一のスレッドが正常に実行されている場合、x=0 を取得し、x=+1 を計算し、メイン メモリに 1 をエコーし、スタック 1 は 1 になり、x=-1 を計算し、メイン メモリに 0 をエコーし、スタック 1 は 0 になります。 複数のスレッドが同時に実行される場合、結果は予測できません。この構造のため、x=+1 を実行すると、スタック 1 は x=1 になり、スタック 2 は実行する時間がなく、スタック 1 はすでに x=1 をメイン メモリに書き込んでおり、スタック 2 とスタック 3 がそれを受け取った後、初期値は 0 ではなく 1 になる可能性があり、プログラムが混乱します。 したがって、スレッドの安全性を確保するために、Java には多くのロックが登場します。 ランタイムデータ領域PCレジスタ ---- スレッド非公開PC レジスタはプログラム カウンター (プログラム カウンター レジスタ) とも呼ばれ、より小さなメモリ空間です。その機能は、現在のスレッドによって実行されたバイトコードのシグナルインジケーターと見なすことができます。 各JVMスレッドには独自のPCレジスタがある いつでも、JVM スレッドは 1 つのメソッドのコードのみを実行します。このメソッドはスレッドの現在のメソッドと呼ばれます。 メソッドが Java メソッドの場合、PC レジスタには JVM が実行しているバイトコード命令のアドレスが保持されます。 メソッドがネイティブの場合、PC レジスタの値は未定義になります。 このメモリ領域は、Java 仮想マシン仕様で OutOfMemoryError 条件が指定されていない唯一の領域です。 Java 仮想マシン スタック ---- スレッド プライベートPC レジスタと同様に、Java 仮想マシン スタックもスレッド専用です。各 JVM スレッドには独自の Java 仮想マシン スタックがあり、これはスレッドと同時に作成され、スレッドと同じライフ サイクルを持ちます。 仮想マシン スタックは、Java メソッド実行のメモリ モデルを記述します。各メソッドが実行されると、ローカル変数テーブル、オペランド スタック、動的リンク、メソッド終了などの情報を格納するスタック フレームが同時に作成されます。各メソッドが呼び出されてから実行されるまでのプロセスは、スタック フレームが仮想マシン スタックにプッシュされてからスタックからポップされるまでのプロセスに対応します。 JVM スタックは固定サイズとして実装することも、計算に基づいて動的に拡張することもできます。 固定サイズの JVM スタック設計を使用する場合、スレッドの作成時に各スレッドの JVM スタック容量を個別に選択する必要があります。 JVM 実装では、JVM スタックの初期容量を調整する手段を提供する必要があります。 動的拡張および縮小の JVM スタック方式を採用する場合は、最大容量と最小容量を調整する手段を提供する必要があります。 JVM スタック例外: StackOverflowError: スレッドによって要求されたスタック容量が JVM で許可された最大容量を超えた場合にスローされます。 OutOfMemoryError: JVM スタックを動的に拡張できるが、拡張しようとしたときに拡張を完了するために要求できるメモリが不足している場合、または新しいスレッドを作成するときに対応する仮想マシン スタックを作成するためのメモリが不足している場合は、このエラーがスローされます。 ローカルメソッドスタック ---- スレッドプライベートJava 仮想マシンは、ネイティブ メソッド (Java 以外の言語で記述されたメソッド) の実行をサポートするために従来のスタックを使用する場合があります。このスタックはネイティブ メソッド スタックです。 JVM がネイティブ メソッドをサポートしておらず、従来のメソッド スタックに依存していない場合は、ネイティブ メソッド スタックをサポートする必要はありません。 ネイティブ メソッド スタックがサポートされている場合、このスタックは通常、スレッドの作成時にスレッドごとに割り当てられます。 異常事態: StackOverflowError: スレッドによって要求されたスタック容量がローカルメソッドスタックで許可された最大容量を超えた場合にスローされます。 OutOfMemoryError: ネイティブ メソッド スタックを動的に拡張でき、拡張アクションが試行されたが、拡張を完了するために現在使用できるメモリが不足している場合、または新しいスレッドの作成時に対応するネイティブ メソッド スタックを作成するために使用できるメモリが不足している場合、Java 仮想マシンは OutOfMemoryError 例外をスローします。 ジャベヒープ -- スレッド共通これは通常、Java チューニングと呼ばれます。 JVM では、ヒープはすべてのスレッドで共有できるランタイム メモリ領域であり、すべてのクラス インスタンスとデータ オブジェクトにメモリが割り当てられる領域でもあります。 Java ヒープは、仮想マシンの起動時に作成されます。ヒープにはさまざまなオブジェクトが格納されており、これらのオブジェクトは自動ストレージ管理システム (一般に「ガベージ コレクター」とも呼ばれます) によって管理されます。これらのオブジェクトは明示的に破棄する必要はなく、破棄することもできません。 Java ヒープ容量は固定サイズにすることも、必要に応じて動的に拡張したり、スペースが不要になったときに自動的に縮小したりすることもできます。 Java ヒープによって使用されるメモリは、論理的に連続していれば、物理的に連続している必要はありません。 JVM 実装では、Java ヒープの初期容量を調整する手段と、動的に拡張および縮小可能なヒープの場合は最大容量と最小容量を調整する手段をプログラマーに提供する必要があります。 Java ヒープ例外: OutOfMemoryError: 実際に必要なヒープが自動メモリ管理システムが提供できる最大容量を超えた場合にスローされます。 メソッド領域 - スレッド共通メソッド領域は、すべてのスレッドで共有できるランタイム メモリ領域です。実行時定数プール、フィールドとメソッドのデータ、コンストラクタと通常のメソッドのバイトコードの内容、クラス、インスタンス、およびインターフェースを初期化するときに使用されるいくつかの特別なメソッドなど、各クラスの構造情報を格納します。 メソッド領域は、仮想マシンの起動時に作成されます。 メソッド領域の容量は固定サイズにすることも、プログラムの実行時に動的に拡張したり、必要なスペースがなくなったら自動的に縮小したりすることもできます。 メソッド領域は実際のメモリ空間では連続していない可能性があります。 Java 仮想マシンの実装では、プログラマーまたはエンド ユーザーにメソッド領域の初期容量を調整する手段を提供する必要があります。動的に拡張および縮小できるメソッド領域の場合、実装では最大容量と最小容量を調整する手段を提供する必要があります。 Java メソッド領域例外: OutOfMemoryError: メソッド領域のメモリ空間がメモリ割り当て要求を満たすことができない場合、Java 仮想マシンは OutOfMemoryError 例外をスローします。 JVM メモリ割り当て実際にはメモリ割り当てには 3 つの種類がありますが、JVM の場合は 2 つだけです。
チューニング プロセス中に、-Xss というパラメーターがあることがわかります。このパラメーターのデフォルトは 1m です。このメモリはスタック メモリの割り当てです。メモリ領域が十分でないため、スタック OutOfMemory エラー メモリ オーバーフローが発生します。通常、メソッド内に数十万行のコードがあり、それをそこにプッシュし続けるとスタックがオーバーフローする場合を除き、これほど大きなスタックは存在しません。スタック メモリの割り当てによって、スレッドの数が直接決まります。たとえば、デフォルトで 1m がある場合、システムによって合計 512m が提供されるため、最大 512 個のスレッドを割り当てることができます。メモリがあまりないため、システムはそれ以上割り当てることができません。たとえば、tomcat、resin、jboss などにはスレッドの最大数があります。これに応じて調整する必要があります。 100万に調整しても意味がありません。このような大きな数を割り当てることはできません。調整が小さすぎると、全体的なパフォーマンスを発揮できなくなります。これを調整することは CPU に関係します。妥協点を見つける必要があります。アプリケーションが IO 集約型か CPU 集約型かに応じて、-Xss の値を調整します。主にいくつかのパラメータとローカル変数がここに保存されます。たとえば、コードを書くときには始まりと終わりがあり、ここには int x=1 y=0 のように多くの変数が定義されている必要があります。このメソッド内にある限り、計算を実行してこれを保存する必要があり、プログラムが終了したときにのみ破棄できるため、これはローカル変数です。スレッドはプライベートなので、この種のパラメータではスレッドセーフティの問題は発生しません。
Java のヒープは、クラスのオブジェクトにスペースが割り当てられる実行時データ領域です。これらのオブジェクトは、new、newarray、anewarray、multianewarray などの命令によって作成され、プログラム コードによる明示的な解放は必要ありません。ヒープはガベージコレクションによって管理されます。ヒープの利点は、メモリ サイズを動的に割り当てることができ、実行時にメモリを動的に割り当てるため、事前にコンパイラに有効期間を伝える必要がないことです。 Java のガベージ コレクターは、これらの未使用データを自動的に収集します。しかし、実行時にメモリを動的に割り当てるため、アクセス速度が遅くなるという欠点があります。 JVMヒープ構造(図1) 1.若い 若い世代は3つの領域に分かれています。エデンエリア 1 つとサバイバーエリア 2 つ。ほとんどのオブジェクトはエデンエリアで作成されます。 Eden エリアがいっぱいになると、生き残ったオブジェクトは Survivor エリア (2 つのうちの 1 つ) にコピーされます。サバイバー エリアがいっぱいになると、このエリア内の生き残ったオブジェクトが他のサバイバー エリアにコピーされます。 Survivor 領域もいっぱいになると、最初の Survivor 領域からコピーされ、その時点でまだ生きているオブジェクトが古い領域にコピーされます。 2 つの Survivor 領域は対称的で順序がないため、同じ領域に Eden からコピーされたオブジェクトと以前の Survivor からコピーされたオブジェクトが同時に含まれる場合があり、最初の Survivor 領域からコピーされたオブジェクトのみが古い領域にコピーされることに注意してください。さらに、サバイバーエリアの 1 つは常に空です。 2.古い(旧世代) 古い世代には、若い世代から生き残ったオブジェクトが保存されます。一般的に、古い世代には寿命の長いオブジェクトが保存されます。 3.永続的: (永続的生成) メソッド領域とも呼ばれ、Java クラスやメソッドなどの静的ファイルを格納するために使用されます。永続的な生成はガベージ コレクションに大きな影響を与えませんが、一部のアプリケーションでは、Hibernate などの一部のクラスを動的に生成したり呼び出したりすることがあります。この場合、実行プロセス中に新しく追加されたクラスを保存するために、比較的大きな永続的な生成スペースを設定する必要があります。永続世代のサイズは -XX:MaxPermSize= で設定されます。 たとえば、プログラム内でオブジェクトが生成されると、若い世代の通常のオブジェクト用にスペースが割り当てられます。オブジェクトが大きすぎる場合は、古い世代に直接生成されることがあります (プログラムの実行中に、メッセージの送受信ごとに 10 MB のスペースが生成され、このメモリの部分が古い世代に直接割り当てられることが確認されています)。若い世代のスペースが割り当てられると、メモリのリサイクルが開始されます。メモリの大部分はリサイクルされ、生き残ったメモリの一部は Survivor の from 領域にコピーされます。複数回のリサイクル後、from 領域のメモリも割り当てられると、メモリのリサイクルも発生し、残りのオブジェクトが to 領域にコピーされます。 to 領域がいっぱいになると、メモリのリサイクルが再度発生し、残っているオブジェクトが古い領域にコピーされます。 通常、JVM メモリのリサイクルについて話すときは、常にヒープ メモリのリサイクルを指します。実際、ヒープ内のコンテンツのみが動的に割り当てられるため、上記のオブジェクトの若い世代と古い世代は JVM のヒープ領域を参照しますが、永続世代はヒープに属さない MethodArea を参照します。 Java ヒープ構造とガベージコレクション図2 Direct Momery は厳密に言えばヒープでもあります。これは物理メモリの一部であり、オペレーティング システム メモリに分割できます。比較的高速で、JVM を経由しません。メモリ マッピングは Java で実装されており、より高速です。 CodeCache はバイトコードとクラス情報の一部を保存します。 パーマネントジェネレーション空間メソッド領域は、厳密に言えばヒープにも属する エデンスペース サバイバースペースゾーン 永住世代の古いエリア JVM GC 管理 チューニングのほとんどは、どのようにリサイクルするかに関するものです。マイナー GC ではエデン スペースとサバイバー スペースがリサイクルされ、フル GC ではすべての領域がリサイクルされます。 どの GC を使用する場合でも、リサイクル プロセス中に一時停止が発生します。リサイクル プロセス中はユーザー スレッドが動作しないため、プログラムが停止する原因になります。これは変えられない事実であり、避けることはできません。ただし、一時停止時間の長さは最適化できます。 原則として、Full GC は発生しません。すべてのエリアを 1 回実行する必要があります。 Full GC が発生すると、アプリケーションは使用できなくなります。 JVM ヒープ構成パラメータ1. -Xms 初期ヒープサイズ デフォルトの物理メモリは64/1(<1GB)で、1G未満が推奨されますが、アプリケーションのビジネスに応じて調整できます。 2. -Xmx 最大ヒープサイズ デフォルトの物理メモリは 4/1 (<1GB) ですが、1G 未満にすることが推奨されます。実際には、4GB を超えないようにすることをお勧めします (そうでないと多くの問題が発生します)。 3. 一般的には-Xms= -Xmxを設定することをお勧めします。 利点は、GC後に毎回ヒープサイズを調整する必要がなくなり、システムメモリ割り当てのオーバーヘッドが削減されることです。 4. ヒープ全体のサイズ = 若い世代のサイズ + 古い世代のサイズ + 永久世代のサイズ (永久世代のスペース領域もフル GC によってリサイクルされます) JVM 若い世代図3 1. 新世代 = エデンエリア 1 つとサバイバーエリア 2 つ 2. -Xmn 若い世代のサイズ 若い世代のサイズを設定します。たとえば、-Xmn=100mの場合、新しい世代は100mになり、共有されます。 3. -XX:新しい比率 若い世代 (Eden と 2 つの Survivor 領域を含む) と古い世代 (永続的な世代を除く) の比率は Xms=Xmx であり、Xmn が設定されている場合はこのパラメータを設定する必要はありません。 4. -XX:生存率 Eden 領域と Survivor 領域のサイズ比を 8 (デフォルトは 8) に設定すると、2 つの Survivor 領域と 1 つの Eden 領域の比率は 2:8 になり、1 つの Survivor 領域が若い世代全体の 1/10 を占めることになります。 たとえば、新しい世代が100mで、-XX:SurvivorRatioが8に設定されている場合、E = 80m S0 = 10m S1 = 10m (1/10) 5. JVMによって割り当てられたJavaオブジェクトを格納するために使用される Java 終身在職世代図4 1. 古い世代 = ヒープ全体 - 若い世代のサイズ - 永久世代のサイズ 若い世代は上記の-xmnで設定されるパラメータであり、永久世代パラメータはデフォルトで0に設定されます。 2. ガベージ コレクションによってリサイクルされなかった若い世代のオブジェクトは、古い世代にコピーされます。 つまり、オブジェクトは一度回収された後、どこかで参照され、使用され、リサイクルできないことが判明すると、投入されます。一般的には、複数回リサイクルされます。 E エリアからのリサイクル処理では、まず S0 または S1 でリサイクルされ、その後再び S0 または S1 でリサイクルされます。リサイクルできない場合は、古いエリアに置かれます。 3. 古い世代に保存されているオブジェクトは、若い世代に保存されているオブジェクトよりもはるかに古く、大きなオブジェクトが不足することはありません。 インターネット企業にとって、最も一般的に使用されるオブジェクトは「キャッシュ」されます。キャッシュは一般に弱参照を使用しますが、ヒープ全体のメモリが不足してメモリがクラッシュするのを防げない限り、弱参照は簡単にはリサイクルされません。強い参照はガベージ コレクション メカニズムに関連しています。一般的に、オブジェクトが一連の強い参照によって参照できる場合、そのオブジェクトはガベージ コレクション メカニズムによってリサイクルされないことを意味します。 先ほど、キャッシュ オブジェクトは一般に弱参照であると述べました。一部のデータが失われても問題ありません。これらは、システムのパフォーマンスを向上させるためにのみキャッシュに格納されます。ただし、ある日メモリが足りなくなり、キャッシュがオブジェクトの大部分を占めてしまった場合、それらをリサイクルしないと、システム全体が利用できなくなり、サービス全体が利用できなくなります。リサイクルすればデータベースから取得できます。速度は遅くなるかもしれませんが、サービスの利用可能性は低下しません。 たとえば、割り当てられたばかりのオブジェクトは、一時的に OLD 領域にあります。最初は、メモリ領域の一部がキャッシュによって占有されています。一般的に、キャッシュの設計には初期値が存在します。 Java の場合、より一般的なキャッシュは自動的に拡張および縮小できます。 図(4)に示すように、OLD領域全体50Mのうち45Mはキャッシュによって占有されており、リサイクルされないため、OLD領域全体のうち5Mのみが使用可能です。 E エリアに 40M がある場合、S0 は 10M を割り当て、S1 も 10M を割り当てます。理想的には、E 領域から S0、S1 から旧世代までのサイズは 1M 未満なので、5M で十分であり、FULL GC やメモリ オーバーフローは発生しません。オブジェクトが 5M を超えると (たとえば 10M のデータ)、格納できなくなり、FULL gc が表示されます。 FULL gc はキャッシュ全体を収集し、キャッシュされたデータは瞬時に消えてしまいます。次に、10M のデータを入れます。これは弱い参照であり、サービスの低下として理解できます。強い参照の場合は、そのままハングアップします。 4. 新しく作成されたオブジェクトは、古い世代に直接入ることもあります。 4.1.大きなオブジェクトは起動パラメータで設定できる -XX:PretenureSizeThreshold=1024 (バイト単位、デフォルトは 0 で、すべてのデフォルトが新しい世代にあることを意味します) は、サイズが制限を超えると、新しい世代は割り当てられなくなり、古い世代に直接割り当てられることを示します。 4.2.大きな配列オブジェクトの場合、配列内の外部オブジェクトへの参照はありません。 5. 古い世代のサイズに関する構成パラメータがない Java 永久世代 (perm generation) 1. 永続世代 = ヒープ全体 - 若い世代のサイズ - 古い世代のサイズ 2. -XX:PermSize 最小 -XX:MaxPermSize 最大 永続世代のサイズを設定します。永続世代のサイズを調整するとヒープ メモリによって fgc がトリガーされるため、通常は -XX:PermSize を -XX:MaxPermSize と同じ値に設定することをお勧めします。 3. クラスとメソッドのメタデータを保存します。そのサイズは、プロジェクトの規模とクラスおよびメソッドの数に関係します。一般的には128Mで十分です。設置の原則は、スペースの30%を確保することです。 当初は128Mに設定されていました。プログラムを実行すると、Java には lib と呼ばれる場所があり、そこに多くのクラス ライブラリが配置されます。これらのクラス ライブラリのすべてがロードされるわけではありません。使用時またはシステムの初期化時に、その一部のみがロードされます。たとえば、100M が占有されていますが、ビジネスの実行に伴い、クラス ライブラリは動的に削除されます。 リフレクションを通じていくつかのクラス ファイルをライブラリに追加します。このようにして、あなたの記憶力は増加し続けます。 128M に達するとハングし、メソッド領域のオーバーフローが報告されます。どうすればいいですか? 256M まで増やして監視し、しきい値を超えたら再度増やします。簡単な方法はそれを増やすことです。さらに、JDKにはリサイクルできるGCがある。 ダウンタイムを許容できる場合は、電力消費を増やします。シンプルで高速、そして問題を解決します。 4. 永久世代リサイクル方式 4.1.定数プール内の定数、役に立たないクラス情報、定数のリサイクルは非常に簡単で、参照がない場合はリサイクルできます。 たとえば、定数 = 5 は値を意味します。リサイクルされても参照されないことが判明した場合は、リサイクルされます。 4.2.不要なクラスをリサイクルするには、次の 3 つの点を確保する必要があります。 クラスは定数とは異なります。クラスには、このクラスがあのクラスを参照するなど、さまざまなものが含まれることがあります。 クラスのすべてのインスタンスがリサイクルされました クラスをロードした ClassLoader がリサイクルされました クラスオブジェクトのクラスオブジェクトは参照されていません(つまり、リフレクションを通じてクラスが参照される場所はありません) JVM ガベージコレクションアルゴリズム1. 参照カウントアルゴリズム 各オブジェクトには参照カウント属性があります。新しい参照が追加されると、カウントが 1 増加します。参照が解放されると、カウントが 1 減少します。カウントが 0 に達すると、リサイクルできます。この方法は単純ですが、オブジェクト間の循環参照の問題を解決することはできません。もう一つの問題は、正確なカウントの問題をどのように解決するかということです。 この方法はもう使われていない 2. ルート探索アルゴリズム 検索は GC ルートから始まり、下方向に進みます。検索によってたどられるパスは参照チェーンと呼ばれます。オブジェクトに GC ルートへの参照チェーンがない場合、そのオブジェクトは使用できないことが証明されます。到達不可能なオブジェクトです。 Java 言語では、GC ルートには次のものが含まれます。 仮想マシン スタックで参照されるオブジェクト。 メソッド領域内のクラス静的属性エンティティによって参照されるオブジェクト。 メソッド領域内の定数によって参照されるオブジェクト。 ネイティブ メソッド スタック内の JNI によって参照されるオブジェクト。 JVM ガベージコレクションアルゴリズム1. コピーアルゴリズム
ルート コレクションからスキャンします。これは、先ほど説明した GC-Roots コレクション アルゴリズムです。そこから参考文献の確認を始めましょう。参照がない場合、アルゴリズムの実行を開始し、生き残ったオブジェクトを新しいオブジェクト(S0またはS1)にコピーします。 2. マークアンドスイープアルゴリズム マーク アンド スイープ アルゴリズムは、図に示すように、ルート セットからスキャンし、生き残ったオブジェクトをマークしてから、スペース全体でマークされていないオブジェクトをスキャンしてリサイクルします。 マークスイープ アルゴリズムでは、オブジェクトの移動は必要なく、デッド オブジェクトのみを処理します。ライブオブジェクトが多数ある場合に非常に効率的です。ただし、マークスイープ アルゴリズムは、使用されていないオブジェクトを直接リサイクルするため、メモリの断片化が発生します。 高齢者のリサイクルに適している マークスイープ アルゴリズムは、マークスイープ アルゴリズムと同じ方法を使用してオブジェクトをマークしますが、クリアする場合は異なります。残存していないオブジェクトによって占有されているスペースを再利用した後、残存しているすべてのオブジェクトは左側の空きスペースに移動され、対応するポインターが更新されます。 マークスイープアルゴリズムは、マークスイープアルゴリズムに基づいてオブジェクトを移動するため、コストは高くなりますが、メモリの断片化の問題を解決します。 用語集 1. 連続リサイクル gcシングルスレッドメモリリカバリはすべてのユーザースレッドを一時停止します 2. 並行リサイクル コレクションとは、複数の GC スレッドが並行して動作することを意味しますが、この時点ではユーザー スレッドは中断されます。したがって、Seral コレクターはシリアル、Parallel コレクターはパラレル、CMS コレクターはコンカレントです。 3. 同時リサイクル これは、ユーザースレッドとGCスレッドが同時に実行されることを意味します(必ずしも並行しているわけではなく、交互に並んでいる可能性がありますが、一般的に同時に実行されます)。ユーザースレッドを一時停止する必要はありません(実際、ユーザースレッドは依然として一時停止する必要がありますが、非常に短いですが、GCスレッドは別のCPUで実行されます) シリアルリサイクルは、並列リサイクルと同時リサイクルを区別する必要があります。これは非常に重要です。 GCを選択するときは、アプリケーションシナリオに従って選択します。 JVM Common Garbage Collector上の写真は、ホットスポットのコレクターです。中央の水平線は生成を表し、接続線はそれらを組み合わせて使用できることを示しています。 若い世代のエリアがあります シリアル parnew scurrency 平行スカベンジ 古い世代のエリアがあります CMS シリアル古い 平行古い G1はまだ成熟しておらず、老年世代と老世代に適しています。 シリアルコレクターこれは、1つのCPUまたは1つのスレッドしか使用できないシングルスレッドコレクターで、ごみ収集を完了します。ゴミコレクションを実行するとき、コレクションが完了するまで他のすべての作業スレッドを一時停止する必要があります。 欠点:世界の停止 利点:シンプル。単一のCPUの場合、マルチスレッドインタラクションオーバーヘッドがないため、より効率的になります。これは、クライアントモードのデフォルトの新世代コレクターです。 新世代のシリアルコレクター 1. -xx:+useSerialgcで有効にします メモリ回復のためのシリアル新しい +シリアル古いコレクターの組み合わせ 2。複製アルゴリズムを使用します。 3。排他的なごみ収集。 1つのスレッドが連続的にGCを実行します。他のワーカースレッドは一時停止されます。 古い世代のシリアルコレクター 1。 -xx:useserialgcを有効にします メモリ回復のためのシリアル新しい +シリアル古いコレクターの組み合わせ 2。マーク圧縮アルゴリズムを使用します 3。シリアル、排他的なガベージコレクター。 記憶は比較的大きいため、回復は新世代よりも遅いため パーネューコレクター(パラレルコレクター) パラレルコレクターは、排他的なコレクターでもあります。収集プロセス中、アプリケーション全体が一時停止されます。ただし、パラレルコレクターはガベージコレクションに複数のスレッドを使用するため、生成する一時停止時間は、より強力な並行性機能を備えたCPUで短くなります。 単一のCPUまたは弱い並行性を備えたシステムでは、並列コレクターの効果はシリアルコレクターの効果よりも優れていません。マルチスレッドの圧力により、実際のパフォーマンスはシリアルコレクターのパフォーマンスよりも悪い可能性があります。 新世代のパーネューコレクター 1。 -xx:+useparnewgcが有効になります 新世代はパラレルコレクションコレクターを使用し、古い世代はシリアルコレクターを使用します 2。 -xx:Parallelgcthreadsスレッドの数を指定します デフォルトでは、ゴミ収集のパフォーマンスに影響を与える過度のスレッドの数を避けるために、スレッドの数をCPUの数に等しく設定することをお勧めします。 3.複製アルゴリズムを使用します。 4。並列、排他的なガベージコレクター。 新世代の平行スカベンジコレクター 1。スループット優先コレクター CPUスループット、つまりユーザーコードの実行時間 /合計時間に焦点を当てます。たとえば、JVMは100分間実行され、そのうち99分はユーザーコードの実行に、ガベージコレクションに1分間費やされます。スループットは99%です。このコレクターは、CPUを最も効率的に使用でき、バックグラウンド操作を実行するのに適しています。 2。 -xx:+useparallelgcが有効になります パラレルスカベンジ +シリアル古いコレクターを使用してゴミを収集します。これはサーバーモードのデフォルト値でもあります 3。 -xx:gctimeration ユーザー実行時間の割合を合計時間に設定するには、デフォルトは99です。つまり、1%の時間がごみ収集に使用されます 4。 -xx:maxgcpausemillis GCの最大一時停止時間を設定します 5。複製アルゴリズムを使用します 古い世代の平行な古いコレクター 1。 -xx:+useparalleloldgcが有効になっています パラレルスカベンジ +パラレルの古いコレクターを使用して収集します 2。マークアンドソートアルゴリズムを使用します。 3。並列、排他的なガベージコレクター。 CMS(同時マークスイープ)コレクター 操作プロセスは4つの段階に分割されます。 CMS初期マーク:値は、GCルーツが直接関連付けられるオブジェクトをマークします。 同時マーキング(CMSコンカレントマーク):GCルートストレスを実行するプロセス。 CMSの備考:同時マーキング中にユーザープログラムの継続的な実行により、マーキングが変更されたオブジェクトのマーキングを修正します。 同時スイープ(CMSコンカレントスイープ): マーキングと再マークの段階では、世界の停止が必要です。コレクターは、同時マーキングと同時クリアリングプロセス中にユーザースレッドを使用でき、プロセス全体で最も長い時間がかかります。 CMS(同時マークスイープ)コレクター 1。マークスイープアルゴリズム 同時に、マルチスレッドの同時リサイクルを使用するゴミコレクターです 2。 -xx:Parallelcmsthreads CMSスレッドの数を手動で設定します。 CMSによって開始されたスレッドのデフォルト数は(Parallelgctherads+3)+3/4)です これがその式です。一般に、IO集約型CPUの場合、コアの数に2 +1を掛け、CPU集約型CPUの場合、CPUコアの数に+1を掛けます。 3。 -xx+useconcmarksweepgcがオンになります メモリ回復のためにParnew+CMS+シリアル古いコレクターの組み合わせを使用し、CMSが「同時モード障害」で失敗した後、シリアル古いものがバックアップコレクターとして使用されます。 障害後、完全なGCがトリガーされます。この状況を回避するには、構成する必要があります。完全なGCがトリガーされる2つの状況があります。プロモーションの失敗と同時モードの障害です。 古い世代GCにCMSを使用するプログラムの場合、GCログに2つの条件があるかどうかに特に注意してください。プロモーションの失敗と同時モード障害。これらの2つの条件が発生すると、可能性があります 完全なGCをトリガーします。 プロモーションの失敗は、マイナーGC中に生存者スペースがいっぱいになり、オブジェクトを古い世代にのみ配置できる場合に発生しますが、古い世代はオブジェクトを保持できません。同時モードの障害は、ときに発生します CMS GCの実行中、一部のオブジェクトは同時に古い世代に配置され、この時点では古い世代がスペースが不足しています(「スペースの不足」は、CMS GCの間にあまりにも多くのフローティングゴミが原因で、一時的なスペースが不足し、完全なGCを引き起こすことができます)。 対応する測定値は、生存空間を増やし、古い世代空間を増やしたり、同時GCのトリガーの比率を下げたりすることです。 4。 -XX:CMSINITIATINGOCCUPANCYFRACTION CMSコレクターを設定して、古い世代のスペースを使用した後、ゴミコレクターをトリガーします。デフォルト値は68%で、CMSコレクターを使用する場合にのみ有効です。 -xx:cmsinitiatingoccupancyfraction = 70 (一般的に、それは70%です。設定が高すぎると、障害が発生する可能性があります。障害が低すぎると、障害が頻繁に発生する可能性があります。比率を見つけて、GCログを分析して要件を満たしているかどうかを確認できます。) 5。 -xx:+ usecmscompactatullcollection CMSコレクターはフラグメントを生成するため、このパラメーターは、ゴミコレクターの後にメモリの解体プロセスが必要かどうかを設定します。 CMSコレクターを使用する場合にのみ有効です。 6。 -xx:+cmsfullgcbeforecompaction CMSコレクターをセットアップして、いくつかのゴミコレクションの後にメモリの解体プロセスを実行します。 usecmscompactatullcollectionパラメーター 7. -xx:cmsinitiatingpermoccupancyfraction 使用してトリガーパーマーのレートを設定する、デフォルト92% |
>>: 人工知能からクラウドネイティブまで、NVIDIAはスーパーコンピューティングを開発しています
gcorelabs は近年かなり有名になってきており、独立サーバー、VPS、CDN などの売れ行きが...
はじめに: ウェブサイトの顧客を増やしたい場合は、ウェブサイトをシンプルに保ち、価値あるコンテンツを...
エンタープライズ データのバックアップ、リカバリ、アーカイブ、クラウド サービスの世界的リーダーであ...
電子商取引企業が共同でコミュニティに進出し、生鮮食品の共同購入やライブストリーミングによって消費者が...
本当に役に立つ情報がないなら、興味深いコンピュータ室を紹介します。今回の主役はオランダにあるサイバー...
CodeGuard は、すべてのウェブマスターが自分のウェブサイトをバックアップおよび復元できる無料...
データベースの専門家である Chris Foot が、データベースをクラウドに移行するときに IT ...
前回の記事では、主にユーザーのニーズの分析とその内容を紹介しました。この記事では、主にほとんどのユー...
10月27日、2017 iResearch A10 ビッグデータサミットにおいて「Intellige...
gcorelabs 極東データセンター ウラジオストクはホスト キャットでテストされ、リリースされま...
長い間マーケティングを勉強してきたあなたは、何か大きなことをする準備ができているに違いありません。あ...
多数の伝統的な企業がインターネットに群がるにつれて、さまざまな業界での競争が特に激しくなり、無料トラ...
「私にとって最も辛いのは、廃棄される在庫1000万相当の契約を自らの手で締結したことだ」と、すでに破...
アプリケーションは通常、専用のシークレット ストアを使用して、キーやトークン、データベース、サービス...
3月12日のウェブマスターネットワーク(www.admin5.com)によると、2月下旬、百度はウェ...