目次
JVMの場所
アプリケーション (Java アプリケーション) は JRE 上で実行され (JRE には JVM が含まれます)、JRE はオペレーティング システム (Windows、Mac) 上で実行され、オペレーティング システムはハードウェア アーキテクチャ (Intel、Spac...) 上で実行されます。
3 つの JVM
- Sun: HotSpot が最も使用されています (私たちが使用しています)
- BEA:JRockit
- IBM:J9VM
JVM アーキテクチャ
JVM チューニング: 99% はメソッド領域とヒープにあり、ほとんどの場合、ヒープがチューニングされます。JNI (Java Native Interface): ネイティブ メソッド インターフェイス
クラスローダー
機能: クラスファイルをロードします。
例: new Student();
(特定のインスタンスはヒープにあり、参照変数名はスタックに配置されます)
- 仮想マシンに付属するローダー
- クラス (ルート) ローダーを開始します。
- 拡張クラスローダー
- アプリケーションローダー
保護者の委任メカニズム
コンセプト
クラスローダが特定の .class ファイルをロードする必要がある場合、まず上位クラスローダにこの作業を委託し、この操作を再帰的に実行します。上位クラスローダがロードしない場合は、クラス自体をロードします。
例
Hello.class
そのようなファイルをロードする場合。
カスタム クラス ローダーに関係なく、最初にそれがAppClassLoaderにロードされているかどうかを確認します。ロードされている場合は、再度ロードする必要はありません。そうでない場合は、親ローダーが取得され、親ローダーのloadClassメソッドが呼び出されます。
同様に、親クラスはまずロードされているかどうかを確認し、ロードされていない場合は上に進みます。この再帰プロセスは、 Bootstrap classLoaderに到達するまで、ロードされているかどうかをチェックし、それ自体でロードすることを選択しないことに注意してください。BootstrapClassLoader
までは親ローダーが存在しないので、この時点でロードできるか考え始めますが、ロードできない場合は子ローダーに沈んで一番下までロードします。それをロードできるローダーがないため、 がスローされます。ClassNotFoundException
効果
- 1. 同じ .class を繰り返しロードしないようにします。上記は委託でお願いします、ロード後、再度ロードする必要はありません。データのセキュリティを確保します。
- 2. core.class が改ざんできないことを確認します。委任により、コアの .class は改ざんされず、改ざんされてもロードされず、ロードされたとしても同じ .class オブジェクトにはなりません。異なるローダーは同じ .class をロードしますが、同じ Class オブジェクトではありません。これにより、クラスの実行の安全性が確保されます。
例: システムレベルのクラス String.java を置き換えたい場合。
実装を改ざんすると、このメカニズムでは、これらのシステムのクラスは Bootstrap classLoader によってロードされます (なぜですか? クラスをロードする必要がある場合、最初にロードしようとするのは BootstrapClassLoader だからです)。そのため、他のクラス ローダーはロードされません。再ロードの機会により、危険なコードの埋め込みをある程度防ぐことができます。
サンドボックスセキュリティメカニズム
サンドボックスを構成する基本コンポーネント
- バイトコード検証ツール (bytecode verifier) は、
Java クラス ファイル .Class が Java 言語仕様に従っていることを確認します。これは、Java プログラムがメモリ保護を実現するのに役立ちます。ただし、コア クラスなど、すべてのクラス ファイルがバイトコード検証を受けるわけではありません。 - クラス ローダー (クラス ローダー)
クラス ローダーは、次の 3 つの方法で Java サンドボックス上で動作します。- 悪意のあるコードが善意のコードに干渉するのを防ぎます; //親委任モード
- 信頼できるライブラリの境界を守ります。
- コードを保護ドメインに配置し、コードが実行できる内容を決定します。
仮想マシンは、異なるクラス ローダーによってロードされたクラスに対して異なる名前空間を提供します。名前空間は一連の一意の名前で構成されます。ロードされた各クラスには名前があります。この名前空間は、クラス ローダーによって維持されるクラスごとに Java 仮想マシンによって作成され、クラス ローダーによって維持されます。お互いに見えません。
クラスローダーによって使用されるメカニズムは、親委任モデルです。
1. 最も内側の JVM のクラス ローダーからロードを開始しますが、同じ名前の外側の悪意のあるクラスはロードできず、使用できません。
2. アクセスドメインはパッケージによって厳密に区別されているため、外層の悪意のあるクラスは組み込みコードを介して内部クラスにアクセスする許可を得ることができず、損傷したコードは当然効果を発揮できなくなります。
- アクセス コントローラー (アクセス コントローラー): アクセス コントローラーは、オペレーティング システムに対するコア API のアクセス権限を制御でき、この制御のポリシー設定はユーザーによって指定できます。
- セキュリティ マネージャー (セキュリティ マネージャー): コア API とオペレーティング システムの間の主要なインターフェイスです。アクセス許可制御を実装するには、アクセス コントローラーよりも高い優先順位が必要です。
- セキュリティ パッケージ: java.security の下のクラスと拡張パッケージの下のクラス。ユーザーは次のような新しいセキュリティ機能をアプリケーションに追加できます。
- セキュリティプロバイダー
- メッセージダイジェスト
- デジタル署名 keytools https (証明書が必要)
- 暗号化
- 識別する
ネイティブ
ネイティブ キーワードを持つメソッドは、Java のスコープに到達できないことを示し、基になる C 言語ライブラリを呼び出す必要があります。
ネイティブ キーワードを持つメソッドはすべてローカル メソッド スタックに入り、その他は Java スタックになります。
JNI: Java ネイティブ インターフェイス (ローカル メソッド インターフェイス)
ローカル メソッド インターフェイス (JNI) を呼び出す機能:
Java の使用を拡大し、Java 用のさまざまなプログラミング言語を統合します。Java
の本来の目的は、C/C++ プログラムを統合することでした。C と C++ が蔓延しています。足場を築きたい場合は、C と C++ を呼び出すプログラムが必要です。 C++. メモリエリア都市で特別に開発されたブロックマーキング地区都市:ネイティブメソッドスタック
ネイティブメソッドスタック
実行エンジン(Execution Engine)の実行時にネイティブメソッドを登録します。JNI (ネイティブ メソッド インターフェイス) 経由で **ネイティブ ライブラリ** にメソッドをロードします。
エンタープライズレベルのアプリケーション、ハードウェア関連のアプリケーションではまれです: Java プログラム駆動のプリンタ、システム管理生産装置など。
PCレジスタ(プログラムカウンタレジスタ)
プログラムカウンタ: プログラムカウンタレジスタ
各スレッドには、スレッド専用のプログラム カウンターがあり、メソッド領域内のメソッド バイトコードへのポインターです(次の命令 (実行される命令コードでもある) を指すアドレスを格納するために使用されます)。実行エンジン 次の命令の読み取りは非常に小さなメモリ空間であり、ほとんど無視できます。
メソッドエリア
メソッド領域はすべてのスレッドによって共有されます。すべてのフィールドとメソッドのバイトコードだけでなく、コンストラクターやインターフェイス コードなどのいくつかの特別なメソッドもここで定義されます。簡単に言えば、すべての定義されたメソッド情報はこの領域に保存され、共有領域に属します。空間;
静的変数、定数、クラス情報(構築メソッド、インターフェース定義)、実行時定数プール(static、final、Class(クラステンプレート)、定数プールなど)はメソッド領域に格納されますが、インスタンス変数はヒープに格納されますメモリ. メソッド領域とは関係ありません。
スタック (Java スタック)
main() が最初に実行され、最後に終了する理由: (main() が最初にスタックにプッシュされるため)
スタック: スタック メモリ、スーパーバイザ プログラムの実行、ライフ サイクル、およびスレッドの同期。
スレッドが終了するとスタック メモリが解放されるため、スタックに関してはガベージ コレクションの問題は発生しません。
スタック ストレージ: 8 つの基本タイプ + オブジェクト参照 + インスタンス メソッド。
スタックの動作原理:スタックフレーム(ローカル変数テーブル+オペランドスタック)は呼び出されるメソッドごとにスタックフレームを持ちます。
スタックがいっぱいで main() を終了できない場合、エラーがスローされます: スタック オーバーフローStackOverflowError
スタック + ヒープ + メソッド領域: インタラクティブな関係
ヒープ
JVM にはヒープ メモリが 1 つだけあり、ヒープのサイズは調整できます。
クラス ローダーはクラス ファイルを読み取った後、通常、すべての参照型をヒープに保存するクラス、メソッド、定数、変数、および実際のオブジェクトを配置します。
ヒープ メモリは 3 つの領域に分割されます。
- 新しい地区 (エデン地区) 若い/新しい
- 退職エリア古い
- 永続領域 Perm、JDK8 以降、永続ストレージ領域の名前が変更されました (メタスペース)
主にイーデンパークと退職者エリアでの GC ガベージコレクション。
メモリがいっぱいであると仮定すると、エラー OOM が報告されます: ヒープ メモリが不足していますOutOfMemoryError:Java heap space
//-Xms8m -Xmx8m -XX:+PrintGCDetails
public static void main(String[] args) {
String str = "javajavajavajava";
while (true){
str += str + new Random().nextInt(888888888)+ new Random().nextInt(21_0000_0000);
}
}
//OutOfMemoryError:Java heap space 堆内存满了
ニューボーンエリア(エデンの園+サバイバーエリア*2)
- クラスが生まれ、成長し、さらには消滅する場所
- エデンの園、すべてのオブジェクトはエデンの園で新しく作成されます
- サバイバー エリア (from、to)、軽い GC がエデンの園を定期的にクリーンアップし、生き残った人がサバイバー エリアに配置されます。サバイバー エリアがいっぱいになった後、重い GC がエデン + サバイバー エリアをクリーンアップします。生存者は退職エリアに収容されます。いっぱいの場合は、OOM を報告します。
注: 調査の結果、オブジェクトの 99% は一時的なオブジェクトであることがわかりました。直接掃除した
高齢者エリア
新エリアの残りは軽いGCでは倒せない
常設エリア
この領域はメモリ内に常駐し、JDK 自体が保持するクラス オブジェクトとインターフェイス メタデータを格納するために使用されます。Java ランタイムの環境またはクラス情報が格納されます。この領域にはガベージ コレクション GCはありません。仮想マシンをシャットダウンすると、このメモリが解放されます。
- jdk1.6: 永続世代より前では、定数プールはメソッド領域にあります。
- jdk1.7: 永続的な世代ですが、定数プールはヒープゆっくりと (永続的な世代に) 縮退します。
- jdk1.8 以降: 永続的な世代はなく、定数プールはメタスペース内にあります。
定数プールは常にメソッド領域にあり、その中の文字列プールは JDK1.7 以降ヒープに保存されました。
永続領域 OOM の例: 多数のサードパーティ jar パッケージをロードするスタートアップ クラス。Tomcat は、大量のアプリケーション、動的に生成された多数のリフレクション クラスをデプロイします。常にロードされています。メモリがいっぱいになるまで、OOM が表示されます。
メソッド領域は非ヒープ (非ヒープ) とも呼ばれますが、概念を区別するために、本質的には依然としてヒープです。
メタスペースは論理的には存在しますが、物理的には存在しません。
ヒープメモリのチューニング
public static void main(String[] args) {
//返回虚拟机试图使用的最大内存
long max = Runtime.getRuntime().maxMemory(); //字节 1024*1024
//返回jvm初始化的总内存
long total = Runtime.getRuntime().totalMemory();
System.out.println("max="+max+"字节\t"+(max/(double)1024/1024+"MB"));
System.out.println("total="+total+"字节\t"+(total/(double)1024/1024+"MB"));
/* 运行后:
max=1866465280字节 1780.0MB
total=126877696字节 121.0MB
*/
//默认情况下,分配的总内存占电脑内存1/4 初始化1/64
}
OOM を報告するにはどうすればよいですか?
-
1. ヒープメモリを拡張してみてください。それでもエラーが報告される場合は、無限ループコードまたはガベージコードがあることを意味します。構成の編集 > VM オプションの追加 > 入力
:-Xms1024m -Xmx1024m -XX:+PrintGCDetails
新生児領域 + 老年領域: 305664K+699392K=1005056K = 981.5M、メタスペース物理学が存在しないことを示します。 -
2. メモリを分析し、どこに問題があるかを確認します (プロフェッショナル ツール)。
コードのどの行が間違っているかを確認します。メモリ スナップショット分析ツール、MAT、Jprofiler
MAT、Jprofiler 関数:- ダンプ メモリ ファイルを分析してメモリ リークを迅速に特定します。
- ヒープ内のデータを取得する
- 大きなオブジェクトを取得する
//-Xms 设置初始化内存分配大小 默认1/64
//-Xmx 设置最大分配内存,默认1/4
//-XX:+PrintGCDetails 打印GC垃圾回收信息
//-XX:+HeapDumpOnOutOfMemoryError //oom DUMP
//-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
public class Demo03 {
byte[] array = new byte[1*1024*1024]; //1m
public static void main(String[] args) {
ArrayList<Demo03> list = new ArrayList<>();
int count = 0;
try {
while (true){
list.add(new Demo03()); //不停地把创建对象放进列表
count = count + 1;
}
} catch (Exception e) {
System.out.println("count: "+count);
e.printStackTrace();
}
}
}
GC(ガベージコレクション)
JVM は GC を実行する際、新世代、生存領域、旧領域の 3 つの領域を一律に再利用するわけではありません。ほとんどの場合、新世代はリサイクルされます
2 種類の GC: ライト GC、ヘビー GC (フル GC、グローバル GC)
参照カウント
通常、JVM は使用されません。大きなプロジェクト オブジェクトが多すぎます。
コピーアルゴリズム
-XX:MaxTenuringThreshold=15
老年期に入る生存条件の数を設定します。
長所: メモリの断片化がなく、メモリ効率が高い
短所: メモリ空間が無駄になる (生存領域は常に空である); オブジェクトが 100% 生きていると仮定すると、コピーのコストが高くなります。
レプリケーション アルゴリズムの最適な使用シナリオ: オブジェクトの生存率が低い場合、新しい領域が使用されます。
マーククリア
利点: 追加のスペースは必要なく、コピー アルゴリズムが最適化されます。
短所: 2 回のスキャンは時間の大幅な無駄であり、メモリの断片化が発生します。
タグ圧縮 (マーククリーンアップ): 再最適化
トリロジー: マーク – クリア – コンプレス
マーク スイープ圧縮: 再最適化
マークがクリアされるたび、またはメモリの断片化がある程度蓄積した場合に圧縮します。
世代別収集アルゴリズム
メモリ オブジェクトのライフ サイクルに従って、メモリはいくつかの部分に分割され、JVM は通常、メモリを新世代と旧世代に分割します。
新しい世代では、多数のオブジェクトが消滅し、少数のオブジェクトが生き残るため、コピー アルゴリズムを使用すると、少数の生き残ったオブジェクトをコピーするコストを支払うだけでコレクションを完了できます。古い世代では、次の理由により、オブジェクトの生存率は非常に高く、追加のスペースはありません
。 割り当て保証が実行されるため、リサイクルにはマーク クリーンアップまたはマーク ソート アルゴリズムが使用されます。
要約する
メモリ効率: レプリケーション アルゴリズム > マーク スイープ アルゴリズム > マーク圧縮アルゴリズム (時間計算量)
メモリの均一性: コピー アルゴリズム = マーク圧縮アルゴリズム > マーク除去アルゴリズム
メモリ使用率: マーク圧縮アルゴリズム = マーククリアアルゴリズム > コピーアルゴリズム
最適なアルゴリズムというものはなく、適切なアルゴリズムのみが存在します (GC は世代別コレクション アルゴリズムとも呼ばれます)。
- 若い世代: 生存率が低く、複製アルゴリズムが使用されます。
- 旧世代: 生存率が高く、面積が広く、マーク-クリア-圧縮。