[JVMの基礎] JVMの基礎

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 は世代別コレクション アルゴリズムとも呼ばれます)。

  • 若い世代: 生存率が低く、複製アルゴリズムが使用されます。
  • 旧世代: 生存率が高く、面積が広く、マーク-クリア-圧縮。

おすすめ

転載: blog.csdn.net/qq_44033208/article/details/132470273