この記事を読んでも、JVM のクラス ローディング メカニズムがまだ理解できませんか?

この記事を読んでも、JVM のクラス ローディング メカニズムがまだ理解できませんか?

[[326861]]

要点を述べる

まず、面接の質問を紹介しましょう

  • 不正解: count1=1;count2=1
  • 正解: count1=1;カウント2=0

なぜ?なぜ?これは、Java クラスのロードのタイミングから始まります。

当初は分析結果を一番下に書くつもりでしたが、最後まで読む忍耐力がないと心配だったので、まずはここで簡単に分析します。以下の分析が理解できない場合は、記事はそれほど長くないので、最後まで読むことを心からお勧めします。

  1. `Single single = Single.getInstance();` はクラスの `Single` を呼び出し、クラスの静的メソッドを呼び出して、クラスの初期化をトリガーします。
  2. クラスがロードされると、準備プロセス中にクラスの静的変数にメモリが割り当てられ、デフォルト値が `single=null count1=0, count2=0` に初期化されます。
  3. クラスの初期化、クラスの静的変数への値の割り当て、静的コードの高速実行。 `single` には `new Single()` の値が割り当てられ、クラスコンストラクタが呼び出されます。
  4. クラスコンストラクタを呼び出した後、`count=1;count2=1`
  5. 引き続き、`count1` と `count2` に値を割り当てます。この時点では`count1`には代入演算がないので`count1`は1ですが、代入演算が実行されると`count2`は0になります。

クラスの読み込み時間

クラスは仮想マシンのメモリにロードされた時点で開始され、メモリからアンロードされた時点で終了します。ライフサイクル全体には、読み込み、検証、準備、解析、初期化、使用、アンロードの 7 つの段階が含まれます。検証、準備、解析の 3 つの部分を総称してリンクと呼びます。

ロード、検証、準備、初期化、アンロードの 5 つのステップの順序は固定されています。場合によっては、Java 言語のランタイム バインディング機能をサポートするために、初期化後に解析フェーズが発生することがあります。

クラスの初期化を開始するタイミング

  1. クラスのインスタンスを作成する
  2. クラスの静的変数(定数を除く [final とマークされた静的変数] 理由: 定数は、コンパイラがフィールドではなく値として扱うため、特別な変数です。コード内で定数変数 (`constant variable`) を使用すると、コンパイラはオブジェクトからフィールドの値をロードするためのバイトコードを生成せず、バイトコードに直接値を挿入します。これは非常に便利な最適化ですが、final フィールドの値を変更する必要がある場合は、そのフィールドを使用するすべてのコードを再コンパイルする必要があります。
  3. クラスの静的メソッドへのアクセス
  4. (`Class.forName("my.xyz.Test")`) のようなリフレクション
  5. クラスを初期化するときに、その親クラスが初期化されていない場合は、まず親クラスが初期化されます。
  6. 仮想マシンが起動すると、main()メソッドを定義するクラスが最初に初期化されます。
  • アクティブ参照: 上記の動作はクラスへのアクティブ参照と呼ばれ、クラスの初期化をトリガーします。
  • 受動参照: 上記の5つの能動参照に加えて、クラスの初期化をトリガーしないクラス参照方法もあります。これをクラスの受動参照と呼びます。

インターフェースをロードするプロセスは、クラスをロードするプロセスとは少し異なります。 `static{}` ブロックはインターフェースでは使用できません。インターフェイスが初期化されるときに、その親インターフェイスのすべてが初期化を完了している必要はありません。親インターフェースが実際に使用されるとき(たとえば、インターフェースで定義された定数を参照するとき)のみ初期化されます。

受動的な引用の例

例1

静的フィールドの場合、フィールドを直接定義するクラスのみが初期化されます。親クラスのフィールドがサブクラスによって参照される場合、親クラスは初期化されますが、サブクラスは初期化されない可能性があります。サブクラスが初期化されるかどうかは、JVM 仮想マシン仕様では明確に指定されておらず、仮想マシンの特定の実装によって異なります。

上記のコードを実行すると、出力は次のようになります。

  1. スーパークラス初期化!
  2. 24です

例2

上記のコードを実行すると、「SubClass init!」上記のDemo#main()メソッドでは、SubClassクラスは初期化されず、SubClass[]配列クラスが初期化されるため、出力されません。 SubClass[] 配列クラスは、Object クラスから継承され、newarray バイトコードによって作成される、要素タイプが SubClass の 1 次元配列を表します。

例3

上記のコードを実行すると、「Constant init!」これは「定数伝播最適化」という概念に関係するため、出力されません。 Demo クラスはコード内で Constant クラスの定数 VALUE を参照しますが、VALUE の実際の値は "Hello World!" です。コンパイルフェーズ中に、Demo クラスの定数プールに配置されます。 Demo クラスが「Hello World!」を使用するたびに、定数の場合は、独自の定数プール内で検索します。 Demo クラスは Constant クラスへのシンボリック参照を保持していないため、Constant クラスは初期化されません。

クラスのロードプロセス

負荷

読み込みフェーズには 3 つのステップがあります。

  1. 完全修飾名でクラスを定義するバイナリバイトストリームを取得します。
  2. バイナリバイトストリームで表される静的ストレージ構造をメソッド領域内のランタイムデータ構造に変換する
  3. メソッド領域内のこのデータへのアクセスエントリとして、メモリ内にこのクラスを表す `java.lang.Class` オブジェクトを生成します。

この段階では、次の 2 つの点に注意する必要があります。

  1. バイナリ バイト ストリームをどこから取得するかの指定はありません。 `.class` 静的ストレージ ファイルから取得したり、`zip、jar` などのパッケージから読み取ったり、データベースから読み取ったり、ネットワークから取得したり、実行時に自動的に生成したりすることもできます。
  2. このクラスを表す `java.lang.Class` オブジェクトをメモリ内でインスタンス化した後は、この `Class` オブジェクトがメソッド `Java` ヒープ内にある必要はありません。一部の仮想マシンでは、`HotSpot` などの `Class` オブジェクトがメソッド領域に配置されます。

確認する

検証は接続フェーズの最初のステップです。検証の目的は、`.class` ファイル内のバイト ストリームに含まれる情報が現在の仮想マシンの要件を満たし、仮想マシン自体のセキュリティを危険にさらさないことを確認することです。

`Java` 言語自体は比較的安全な言語です。 Java コーディングを使用する場合、配列の境界外のデータにアクセスしたり、オブジェクトを実装していない型に変換したりすることはできません。これを行うと、コンパイラはコンパイルを拒否します。ただし、`Class` ファイルは必ずしも `Java` ソース コードからコンパイルされるわけではなく、16 進エディター (`UltraEdit` など) を直接使用するなど、任意の方法で記述できます。有害な「コード」(バイト ストリーム)が直接書き込まれ、仮想マシンがクラスをロードするときにチェックしない場合は、仮想マシンまたはプログラムのセキュリティが危険にさらされる可能性があります。

  1. 仮想マシンによってクラス検証の実装が異なる場合がありますが、通常はファイル形式の検証、メタデータの検証、バイトコードの検証、シンボル参照の検証という 4 つの検証段階が完了します。ファイル形式の検証は、バイト ストリームがクラス ファイル形式の仕様に準拠しており、現在のバージョンの仮想マシンで処理できるかどうかを検証します。たとえば、マジックナンバーが 0xCAFEBABE であるかどうかを確認します。メジャーバージョン番号とマイナーバージョン番号が現在の仮想マシンの処理範囲内にあるかどうか。定数プールにサポートされていない定数タイプがあるかどうか...この検証段階の主な目的は、入力バイト ストリームが正しく解析され、メソッド領域に格納されることを確認することです。この段階での検証後、バイト ストリームはメモリのメソッド領域に格納されるため、次の 3 つの検証段階はすべてメソッド領域の格納構造に基づいています。
  2. メタデータ検証は、バイトコードによって記述された情報の意味分析を行い、記述された情報が Java 言語仕様の要件を満たしていることを確認します。可能な検証には、このクラスに親クラスがあるかどうかが含まれます。このクラスの親クラスが継承が許可されていないクラスを継承しているかどうか。このクラスが抽象クラスでない場合、親クラスまたはインターフェースで実装する必要があるすべてのメソッドを実装しているかどうか...
  3. バイトコード検証の主なタスクは、データフローと制御フローを分析して、検証されたクラスのメソッドが実行時に仮想マシンのセキュリティを危険にさらさないことを確認することです。クラスメソッド本体のバイトコードがバイトコード検証に合格しない場合は、間違いなく問題があります。ただし、メソッド本体がバイトコード検証に合格しても、必ずしも安全であるとは限りません。
  4. シンボリック参照の検証は、仮想マシンがシンボリック参照を直接参照に変換するときに行われます。この変換は「解決フェーズ」で発生します。シンボリック参照内の文字列で記述された権限命名が対応するクラスを見つけることができるかどうかを確認します。指定されたクラスに、メソッド フィールドの記述子と単純名を満たすメソッドとフィールドがあるかどうか。シンボリック参照内のクラス、フィールド、メソッドのアクセシビリティ(`private、protected、public、default`)が現在のクラスからアクセスできるかどうか

検証フェーズは、仮想マシンのクラス ロード メカニズムにとって必ずしも必要なフェーズではありません。実行中のすべてのコードが安全であることが確認された場合は、`-Xverify:none` パラメータを使用してほとんどのクラス検証手段をオフにし、仮想マシン クラスの読み込み時間を短縮できます。

準備する

準備段階では、クラスの静的変数にメモリを割り当て、デフォルト値に初期化します。このメモリはすべてメソッド領域に割り当てられます。クラス内のインスタンス変数のメモリは、準備フェーズでは割り当てられません。インスタンス変数は、オブジェクトがインスタンス化されるときに、オブジェクトとともに Java ヒープ内に割り当てられます。

注意すべき点がいくつかあります:

1. メソッド領域にメモリが割り当てられるのは、インスタンス変数ではなく、クラス変数(`static` によって変更される変数)のみです。インスタンス変数には、オブジェクトとともに Java ヒープ内のメモリが割り当てられます。

2. クラス変数が初期化されると、その型に対応する値 `0` に初期化されます。例えば、次のようなクラス変数がある場合、準備フェーズが完了した後、`val` の値は `123` ではなく `0` になります。 `val` の値は、後述する初期化フェーズの後に `123` にコピーされます。

  1. //準備フェーズでは、値の初期値は 0 です。初期化フェーズでのみ 123 になります。
  2. 公共 静的 整数値=123;

3. 定数の場合、対応する値はコンパイルフェーズ中にフィールドテーブルの `ConstantValue` 属性に格納されるため、準備フェーズが終了した後、定数の値は `ConstantValue` で指定された値になります。たとえば、以下に示すように、準備フェーズが終了した後、`val` の値は `123` になります。

  1. 公共 静的最終int値 = 123;

分析

解決フェーズは、仮想マシンが定数プール内のシンボリック参照を直接参照に置き換えるプロセスです。

シンボリック参照: シンボリック参照では、参照先を記述するために一連のシンボルが使用されます。シンボルは、使用時にターゲットを明確に特定できる限り、任意の形式のリテラルにすることができます。シンボリック参照は仮想マシンによって実装されるメモリ レイアウトとは独立しており、参照されるターゲットは必ずしもメモリにロードされるわけではありません。

直接参照: 直接参照は、ターゲットを直接指すポインター、相対オフセット、または間接的にターゲットを特定できるハンドルになります。直接参照は、仮想マシンによって実装されたメモリ レイアウトに関連します。直接参照がある場合、参照先はメモリ内にすでに存在している必要があります。

初期化

クラスの初期化フェーズは、クラスで定義された Java プログラム コードが実際に実行を開始するフェーズです。初期化とは、単にクラス コンストラクターを呼び出すことを意味します。 ()`プロセスでは、クラスコンストラクター内で、クラス変数が定義された値に初期化され、静的コードブロックの内容が実行されます。以下に、あなたと密接な関係にある開発者が注意すべき点をいくつか示します。

1. クラスコンストラクタ()` は、クラス内に出現するクラス変数を自動的に収集し、静的コード ブロック内のステートメントをマージするコンパイラによって生成されます。コレクションの順序は、ソース ファイルに表示される順序によって決まります。静的コード ブロックは、静的コード ブロックの前に表示されるクラス変数にアクセスできます。静的コード ブロックの後に表示されるクラス変数は、割り当てることしかできず、アクセスすることはできません。例えば次のコード

2. ` ()`クラスのコンストラクタと` ()` インスタンス コンストラクターとは異なり、クラス コンストラクターは親クラスのクラス コンストラクターを表示する必要はありません。子クラスのクラス コンストラクターが呼び出される前に、親クラスのクラス コンストラクターが自動的に呼び出されます。したがって、最初の「 () メソッドは java.lang.Object のクラスコンストラクタです。

3. 親クラスのクラス コンストラクターは子クラスのクラス コンストラクターよりも優先されるため、親クラスの `static{}` コード ブロックも子クラスの `static{}` よりも優先されます。

4. クラスコンストラクタクラスには ()` は必要ありません。クラスにクラス変数と`static{}`がない場合、クラスにはクラスコンストラクタはありません。 ()`

5. インターフェースには `static{}` は存在できませんが、インターフェースにはクラス変数が存在する可能性があり、したがってインターフェースにはクラスコンストラクターも存在する可能性があります。 {}` ですが、インターフェースのクラス コンストラクターはクラスのクラス コンストラクターとは異なります。インターフェイスがクラス コンストラクターを呼び出す場合、親インターフェイス内のクラス変数が使用されない限り、必要がない限り、親インターフェイスのクラス コンストラクターは呼び出されません。インターフェースの実装クラスは、初期化時にインターフェースのクラス コンストラクターを呼び出しません。

6. 仮想マシンはクラスの()`メソッドはマルチスレッド環境で正しくロックされ、同期されます。複数のスレッドが同時にクラスを初期化する場合、1つのスレッドのみがこのクラスのクラスコンストラクタを実行します。 ()` の場合、アクティブスレッドがクラスコンストラクタの実行を完了するまで、他のスレッドはブロックされます。 () 方法

<<:  Amazon SageMaker が中国の AWS 寧夏および北京リージョンで利用可能になりました

>>:  JVM とは何ですか?動作メカニズムと基本原理についての簡単な説明

推薦する

独自の記事を作成し、他の人にコピーしてもらう

以前私が書いた記事「ボトムラインを守ることは、自分自身のオリジナリティを守る方法を教えてくれる」を覚...

独自の ES クラスターをクラウドに移行するための完全なガイド

サービスをクラウドに移行する過程では、企業内で自社構築したミドルウェアなどのサービスの移行も必然的に...

最適化の観点からHTML5の開発を見る

2011 年現在、HTML5 は WEB 業界で最も関心の高い問題の 1 つであると考えられます。フ...

ウェブサイトは情報システムセキュリティレベル保護期限修正通知ソリューションを受け取りました

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています2018年...

ロサンゼルスの 3 つのネットワーク向けの AS4837 回線を備えた Hostyun の VPS の簡単なレビュー

Hostyunは本日、ロサンゼルスのceraデータセンターで、3つの必須ネットワークとChina U...

アリババクラウドは、自社開発の第4世代神龍アーキテクチャを搭載し、RDMA強化インスタンスを含む多数の新製品をリリースした。

12月21日、アリババクラウドは自社開発の神龍アーキテクチャをベースにしたRDMA強化インスタンス、...

Kubernetes クラスターで Iptables を Ipvs に置き換える方法

k8s の kube-proxy はネットワーク プロキシであり、Kubernetes クラスター内...

オンラインプロモーションプランの要素とチャネルを分析!

本日は、「希望する人々へのリーチを最大化するためのプロモーション チャネルの選択方法」についてお話し...

タオバオ検索結果のランキングルールについて議論する

私の友人の中には、Taobao ストアをオープンした人が何人かいます。パートタイムの店長やフルタイム...

モバイルコンピューティングの需要が高まっているのはなぜですか?

モバイル コンピューティングの需要はさらに高まり、人々の働き方を決定づけ、モバイル アプリケーション...

ユーザーとクラウドは敵なのでしょうか?

パブリック クラウド ベンダーのダウンタイム、データ損失、ハードウェア障害、人為的エラーなどのインシ...

B2B業界のウェブサイトのホームページをデザインして、良い第一印象を与える

本レポートの最初の 11 セクションでは、B2B 業界の Web サイトのページ デザインを要素ごと...

共同購入ウェブサイトは独立性を失いつつあり、業界大手の手足となっている

潮が引いたときに初めて、誰が裸で泳いでいるかが分かります。 「何千もの共同購入戦争」を経験した後、共...

Volcano Engine パブリック クラウド シティ共有カンファレンスが成都にやって来て、一緒にクラウドに乗るよう皆さんを招待します。

デジタル化の波が広がる中、クラウドコンピューティングやAI技術は企業のイノベーションと成長を促進する...

検索結果のパーソナライゼーションが SEO に与える影響

パーソナライズされた検索結果により、誰もが目にする検索結果が自分に合わせて調整されているように見える...