単なる面接ではありません - JVM クラスローディングの面接の質問の詳細な説明

面接の質問

質問しながら学ぶのが最も効率的 今回は以下の質問に答えていきます。

クラスローディングとは何ですか?
どのような状況でクラスロードがトリガーされるのでしょうか?
JVM がクラスをロードするプロセスについて教えてください。
変数にメモリが割り当てられるのはいつですか?
JVMのクラスロードメカニズムとは何ですか?
親の委任メカニズムは破られる可能性がありますか? なぜですか?
答えは記事の最後にありますが、原則を読む時間がない場合は、最後まで飛ばして答えを直接読んでください。

詳細な原則クラスの
ライフ
サイクル 次のようなクラスのライフ サイクルについては、誰もがよく知っていると思います。
ここに画像の説明を挿入

しかし、これは、馬河美のように、暗記しても忘れ、忘れても暗記するものですよね?

実際、一度理解すると基本的には忘れることはありません。

ロード
ロードでは主に次の 3 つのことが行われます。

クラス ファイルを検索し (クラスの完全修飾名を通じてこのクラスを定義するバイナリ バイト ストリームを取得)、
それをメソッド領域に配置します (このバイト ストリームで表される静的ストレージ構造をメソッド領域のランタイム データ構造に変換します)。
エントリを開く (メソッド領域内のこれらのデータ構造にアクセスするためのエントリとして、このクラスを表す java.lang.Class オブジェクトを生成します)
一般に、このステップは、クラス ローダーを通じてクラスをメモリに読み取ることです。3番目のステップでオブジェクトが生成されますが、オブジェクトはヒープではなくメソッド領域にあることに注意してください。

つながり
つながりは3つのステップに分かれており、一般的に面接では準備について聞かれることが多いです。

検証
名前が示すように、クラス ファイルのバイト ストリームに含まれる情報が現在の仮想マシンの要件を満たしているかどうかを確認します。

準備
このステップでは、静的変数と静的定数にメモリを割り当て、値を割り当てます。

静的変数にはデフォルト値のみが与えられることに注意してください。たとえば次のようなものです。

public static int value = 123;
Copy
このとき value に代入される値は 123 ではなく 0 です。

静的定数 (static Final で変更) は直接割り当てられます。たとえば次のようなものです。

public static Final int value = 123;
Copy
このときvalueに代入される値は123です。

解析
解析フェーズでは、jvm が定数プールのシンボリック参照を直接参照に置き換えます。

さて...定数プールとは何ですか? シンボリック参照とは何ですか? 直接引用とは何ですか?

定数プールを jvm メモリ構造に配置します。まず、記号参照と直接参照とは何かについて説明します。

シンボリック参照と直接参照 次
のように、Car クラスの run() メソッドを含む Worker クラスがあるとします。

class Worker{ ... public void gotoWork(){ car​​.run(); //Worker クラス内のこのコードのバイナリ表現はシンボリック参照} ...} Copy 解析
フェーズ
の前に、Worker クラスはknow car.run() このメソッドのメモリはどこにあるので、このメソッドを表すために使用できるのは文字列のみです。この文字列には、クラス情報、メソッド名、メソッド パラメータなどの十分な情報が含まれているため、実際に使用する際に対応する場所を見つけることができます。

この文字列はシンボル参照と呼ばれます。

解析段階では、jvm は文字列の内容に従ってメモリ領域内の対応するアドレスを見つけ、シンボリック参照をターゲットを直接指すポインタ、ハンドル、オフセットなどに置き換えます。直接使用されます。

ターゲットを直接指すこれらのポインター、ハンドル、およびオフセットは、直接参照と呼ばれます。

初期化
クラスの初期化の主なタスクは、プログラムによって設定された初期値を静的変数に割り当てることです。

上記の静的変数を思い出してください。

public static int value = 123;
コピー
この手順を完了すると、最終的に value の値は 123 になります。

要約は次のとおりです。
ここに画像の説明を挿入

クラスの初期化の条件
Java 仮想マシン仕様では、クラスを初期化する必要がある状況は次の 5 つだけであると厳密に規定されています。

新しいバイトコード命令を使用してクラスのインスタンスを作成するか、getstatic、putstatic を使用して静的フィールドの値を読み取りまたは設定します (定数プールに配置された定数を除く)。または、静的メソッドを呼び出す場合、対応するクラスは次のようにする必要があります。初期化されます。
java.lang.reflect パッケージのメソッドを使用してクラスへのリフレクション呼び出しを行う場合、クラスが初期化されていない場合は、最初に初期化する必要があります。
クラスを初期化するときに、その親クラスが初期化されていないことが判明した場合、最初に親クラスの初期化がトリガーされます。
仮想マシンが起動するとき、ユーザーはメイン クラス (main() メソッドを含むクラス) を指定する必要があり、仮想マシンは最初にこのクラスを初期化します。
jdk1.7の動的言語サポートを使用する場合、java.lang.invoke.MethodHandleインスタンスの最終解析結果がREF_getStatic、REF_putStatic、RE_invokeStaticのメソッドハンドルであり、このメソッドハンドルに対応するクラスが初期化されていない場合、最初に初期化をトリガーする必要があります。
上記の 5 つのケースに加えて、他のケースではクラスの初期化はトリガーされません。

たとえば、次の状況ではクラスの初期化はトリガーされません。

サブクラスを通じて親クラスの静的フィールドを呼び出します。このとき、親クラスは条件1を満たしますが、サブクラスは条件を満たしません。したがって、親クラスのみが初期化されます。
配列を介してクラスを参照しても、クラスの初期化はトリガーされません。new はクラスではなく配列であるためです。
呼び出しクラスの静的定数は、コンパイル段階で呼び出しクラスの定数プールに格納され、定数を定義するクラスを参照しないため、クラスの初期化はトリガーされません。
クラスローディングメカニズム
クラスローダー
上で述べたように、ローディングフェーズでは「クラスの完全修飾名を通じてこのクラスを記述するバイナリバイトストリームを取得する」必要があります。これがクラスローダーの動作です。

jvm には、次の 3 つのクラス ローダーが付属しています。

クラスローダーを起動します。
拡張クラスローダー。
アプリケーションクラスローダ
それらの継承関係は次のとおりです。
ここに画像の説明を挿入

親の委任
親の委任メカニズムの作業プロセスは次のとおりです。

現在の ClassLoader は、最初にこのクラスがすでにロードされているクラスからロードされているかどうかを照会し、すでにロードされている場合は、最初にロードされたクラスを直接返します。各クラスローダーには独自のロードキャッシュがあり、クラスがロードされるとキャッシュに入れられ、次回ロードするときにそのキャッシュを直接返すことができます。
ロードされたクラスが現在の classLoader のキャッシュに見つからない場合、親クラス ローダーがそのロードを委託されます。親クラス ローダーは同じ戦略を採用し、最初に自身のキャッシュを確認し、次に親クラスの親クラスを委託します。すべての親クラス
ローダーがロードされていない場合は、現在のクラス ローダーによってロードされて独自のキャッシュに入れられ、次回ロード要求があったときに直接返されるようにします。
なぜこれほど複雑なのでしょうか? 自分で処理できないのですか?

保護者による委任には以下のようなメリットがあります。

二重読み込みを避けてください。親がすでにクラスをロードしている場合、子 ClassLoader がそのクラスを再度ロードする必要はありません。
安全のために。String などのコア クラスが置き換えられるのを避けてください。
親の委任の解除
「親の委任」メカニズムは Java によって推奨されるメカニズムにすぎず、必須のメカニズムではありません。

たとえば、JDBC は親の委任メカニズムを破壊します。Thread.currentThread().getContextClassLoader() を通じてスレッド コンテキスト ローダーを取得して Driver 実装クラスをロードし、親委任メカニズムを破壊します。

その理由については、後ほどお話します。

回答
さて、記事の冒頭で提起された質問に答えることができます。丸暗記せずに、理解した上で答えるようにしてください。

クラスローディングとは何ですか?
JVM がクラス名を通じてクラスのバイナリ ストリームを取得した後、クラスをメソッド領域に配置し、エントリ オブジェクトを作成するプロセスをクラス ロードと呼びます。ロード後、クラスはメモリに配置されます。
どのような状況でクラスの初期化がトリガーされますか?
クラスは 5 つのケースで初期化されます。
まず、このクラスがエントリー クラスの場合、初期化されます。
次に、 new を使用してオブジェクトを作成するか、クラスの静的変数を呼び出すと、クラスが初期化されます。ただし、静的定数は考慮されません。
3 番目に、リフレクションを通じてクラスを取得すると、クラスが初期化されます
。 4 番目に、サブクラスが初期化されると、その親クラスも初期化されます。
第 5 に、jdk1.7 の動的言語サポートを使用する場合、静的ハンドルへの呼び出しも初期化されます。
JVMがクラスをロードするプロセスについて教えてください
質問1と同じです。ただし、ここで面接官にクラスのライフサイクルについて聞きたいかどうかを尋ねることもできます。クラスのライフサイクルについて尋ねられたら、「ロード、接続、初期化、使用、アンロード」の 5 つの段階があり、接続は「検証、準備、およびアンロード」の 3 つの段階に分けることができます。分析"。
メモリはいつ変数に割り当てられますか?
準備フェーズ中に静的変数にメモリを割り当てます。
JVMのクラスロードメカニズムとは何ですか?
親委任メカニズムであるクラス ローダーは、最初にその親クラスをロードし、親クラスがロードできない場合は、それを独自にロードします。
親の委任メカニズムは破られる可能性がありますか? なぜ
壊れるのかというと、JDBC がスレッド コンテキスト ローダーを使用して親委任メカニズムを壊すなどです。その理由は、JDBC はインターフェイスのみを提供し、実装は提供しないためです。この質問は、引用文献に記載されています。

おすすめ

転載: blog.csdn.net/m0_54861649/article/details/126628764