この記事を読んでも、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 とは何ですか?動作メカニズムと基本原理についての簡単な説明

推薦する

SEOは考え方を変え、技術的な行き詰まりに陥らないようにする必要がある

SEO 業界は参入障壁が低く、競争がますます激しくなっています。さらに、検索エンジンは SEO 業界...

hosthatch - $32/年/512MB メモリ/20GB SSD/1TB トラフィック/G ポート/3 つのデータセンター

Hosthatch はフロリダ州タンパに登録されています。Facebook でハードウェア機器を公開...

新着: crissic-$4/Kvm/2ip/512m メモリ/25g ハードディスク/2T トラフィック

crissic、KVM に行く時間です。タイトルを正しく読んだとおりです! Crissic は設立当...

ウェブサイトの重量を改善するための一般的な方法

ウェブサイトの重みは、ウェブサイトを評価する上で重要な要素です。ウェブマスターとして、自分が担当する...

itldc - 2.32 ユーロ/無制限トラフィック VPS/ウクライナ\リトアニア\ブルガリア\オランダ\米国

ITL グループは、1995 年から運営されている、非常に歴史のある IT 企業です。早速、同社の ...

extravmはどうですか?米国のOVH(オレゴン州ヒルズボロ)データセンターの高セキュリティVPSの簡単なレビュー

ExtraVMは2000年に設立されたアメリカの企業で、主に高防御VPS事業を展開しています。現在、...

Virtwire - 4.4 USD/年/512 MB RAM/5 GB HDD/1 TB トラフィック/1 GB ポート/オランダ

EvoBurst のサブブランド virtwire.com は、エイプリルフールにオランダのデータセ...

テクノロジーが力を与え、AIマーケティングが飛躍的に進歩

月収10万元の起業の夢を実現するミニプログラム起業支援プラン2016年、AlphaGoが世界囲碁チャ...

一人のSEO、死ぬまで働いても誰も同情しない

SEO 業界では、一人で取り組むことは珍しいことではありません。多くのウェブマスターが複数の役割を担...

簡単な議論:百度ランキングの浮動状況をどう解決するか

最近、誰もが自分のウェブサイトに大きな変化が起きたと感じています。最も明らかな変化は、誰もが最も懸念...

SEO最適化王の主張!

この世に王はいない。この世は不公平だ。この世には競争がある。競争の法則に従うことによってのみ生き残る...

ダンダンの14年間の電子商取引の盛衰の歴史:利益と損失の両方を伴う凸凹の道

1999年に設立されたDangdang.comは、14年間の電子商取引のキャリアの中で浮き沈みを経験...

企業ウェブサイトの降格に影響を与える4つの主な要因の簡単な分析

一般的に、中小企業のウェブサイト所有者は、最初はあまり気にしていませんでした。彼らは自分の企業がより...

sharktech-$99/I3-2100/8G/1.5T/1000M無制限/29IPv4/DDOS保護

Sharktech デンバー データ センターには、DDOS 保護 (デフォルト 15G/14Mpp...

IDC: パブリック クラウドは 2017 年上半期に 28.6% 成長しました。パブリック クラウドがなければ、イノベーションは起こりません。

IDC の世界半期パブリック クラウド サービス追跡レポートの最新結果によると、世界のパブリック ク...