20,000 語の JVM 面接の質問と回答分析 (2020 年の JVM 面接の質問 78 件の要約)

序文

JVMシリーズインタビューのナレッジポイントをマインドマップにまとめて共有しました

1. Java ではメモリ リークが発生しますか? 簡単に説明してください。

ミーティング。ヒープされたデータ構造を自分で実装すると、メモリ リークが発生する可能性があります。

2. 64 ビット JVM では、int の最大長はどれくらいですか?

Javaでは、int型変数の長さはプラットフォームに関係なく32ビットの固定値です。つまり、32 ビットと 64 ビットの Java 仮想マシンでは、int 型の長さは同じになります。

3. シリアル GC とパラレル GC の違いは何ですか?

シリアルとパラレルの両方で、GC が実行されるとストップ ザ ワールドが発生します。それらの主な違いは、シリアル コレクターがデフォルトのコピー コレクターであり、GC の実行時にスレッドが 1 つだけであるのに対し、パラレル コレクターは実行に複数の GC スレッドを使用することです。

4. 32 ビットおよび 64 ビット JVM の場合、int 型変数の最大長はどれくらいですか?

32 ビットと 64 ビットの JVM では、int 型変数の長さは同じで、どちらも 32 ビットまたは 4 バイトです。

5. Java における WeakReference と SoftReference の違いは何ですか?

WeakReference と SoftReference はどちらも GC とメモリの効率を向上させるのに有益ですが、WeakReference が最後の強参照を失うと、その強参照は GC によってリサイクルされますが、ソフト参照はそのリサイクルを防ぐことはできませんが、JVM が実行されるまで遅延する可能性があります。記憶がなくなった。

6. JVM オプション -XX:+UseCompressedOops は何をしますか? なぜ使うのか

アプリケーションを 32 ビット JVM から 64 ビット JVM に移行すると、オブジェクト ポインターが 32 ビットから 64 ビットに増加するため、ヒープ メモリが突然増加し、ほぼ 2 倍になります。これは、CPU キャッシュ (RAM よりもはるかに小さい) 内のデータにも悪影響を及ぼします。64 ビット JVM に移行する主な動機は、最大ヒープ サイズを指定できることと、OOP を圧縮することで一定量のメモリを節約できることだからです。-XX:+UseCompressedOops オプションを使用すると、JVM は 64 ビット OOP の代わりに 32 ビット OOP を使用します。

7. Java プログラムを通じて JVM が 32 ビットか 64 ビットかを判断するにはどうすればよいですか?

この情報を取得するには、sun.arch.data.model や os.arch などのいくつかのシステム プロパティをチェックします。

8. 32 ビット JVM と 64 ビット JVM の最大ヒープ メモリはそれぞれどれくらいですか?

理論上、32 ビット JVM ヒープ メモリは 2^32、つまり 4GB に達しますが、実際にはこれよりもはるかに小さくなります。Windows では約 1.5 GB、Solaris では約 3 GB など、オペレーティング システムによって異なります。64 ビット JVM では、最大ヒープ メモリを指定できますが、理論的には 2^64 という非常に大きな数値に達することができ、実際には、ヒープ メモリ サイズを 100GB まで指定できます。Azul などの一部の JVM でさえ、最大 1000G のヒープ メモリを備えています。

9. JRE、JDK、JVM、JIT の違いは何ですか?

JRE は Java ランタイムの略で、Java 参照を実行するために必要です。JDK は Java Development Kit (Java 開発キット) の略で、Java コンパイラなどの Java プログラム開発ツールであり、JRE も含まれています。JVM は Java 仮想マシン (Java 仮想マシン) の略で、Java アプリケーションの実行を担当します。JIT は Just In Time コンパイルの略で、コードの実行数が一定のしきい値を超えると、Java のバイトコードをネイティブ コードに変換します。たとえば、メインのホット コードがネイティブ コードに置き換えられ、パフォーマンスが大幅に向上します。 Java アプリケーション。

10. Java ヒープ領域と GC について説明しますか?

Java プロセスが Java コマンドによって開始されると、そのプロセスにメモリが割り当てられます。メモリの一部はヒープ領域の作成に使用され、プログラム内でオブジェクトが作成されると、その領域からメモリが割り当てられます。GC は、将来の割り当てのために無効なオブジェクトのメモリを再利用する JVM 内のプロセスです。

11. JVMメモリ領域

JVM メモリ領域は主に、スレッド専用領域 [プログラム カウンタ、仮想マシン スタック、ローカル メソッド領域]、スレッド共有領域 [JAVA ヒープ、メソッド領域]、およびダイレクト メモリに分かれます。

スレッドのプライベート データ領域のライフ サイクルはスレッドのライフ サイクルと同じであり、ユーザー スレッドの開始/終了に応じて作成/破棄されます (ホットスポット VM では、各スレッドはローカル スレッドに直接マッピングされます)。オペレーティング システムに依存するため、メモリ領域のこの部分の格納/非存在は、ネイティブ スレッドの生存/停止の対応に従います)。

スレッド共有領域は、仮想マシンの起動/停止に伴い作成/破棄されます。

ダイレクト メモリは JVM ランタイム データ領域の一部ではありませんが、頻繁に使用されます。JDK 1.4 で導入された NIO は、チャネルとバッファに基づく IO メソッドを提供します。これは、ネイティブ関数ライブラリを使用して、オフヒープ メモリを直接割り当てることができます。 use DirectByteBuffer オブジェクトは、このメモリへの参照として動作します (詳細については、「Java I/O 拡張機能」を参照)。これにより、Java ヒープとネイティブ ヒープの間でのデータの往復コピーが回避されるため、一部のシナリオではパフォーマンスが大幅に向上します。

12. プログラムカウンター(スレッドプライベート)

小さなメモリ空間は、現在のスレッドによって実行されるバイトコードの行番号インジケータです。各スレッドには独立したプログラム カウンターが必要です。このタイプのメモリは、「スレッド プライベート」メモリとも呼ばれます。

Java メソッドが実行されている場合、カウンターに記録されるのは、仮想マシンのバイトコード命令のアドレス (現在の命令のアドレス) です。まだネイティブ メソッドである場合、空です。

このメモリ領域は、仮想マシン内で OutOfMemoryError 条件を指定しない唯一のメモリ領域です。

13. 仮想マシンスタック(スレッドプライベート)

Javaメソッドの実行を記述したメモリモデルで、各メソッドが実行されると、ローカル変数テーブル、オペランドスタック、ダイナミックリンク、メソッド出口などの情報を格納するスタックフレーム(Stack Frame)が作成されます。各メソッドの呼び出しから実行完了までのプロセスは、スタック フレームが仮想マシン スタックにプッシュされ、スタックからポップアウトされるまでのプロセスに対応します。

スタックフレーム(Frame)は、データや一部の処理結果を格納するために使用されるデータ構造であり、動的リンク(Dynamic Linking)、メソッドの戻り値、例外のディスパッチ(Dispatch Exception)を処理するためにも使用されます。スタック フレームは、メソッドが呼び出されたときに作成され、メソッドが終了したときに破棄されます。メソッドが正常に完了するか、異常に完了するか (メソッド内でキャッチされなかった例外がスローされる)、メソッドの終了としてカウントされます。

14. ローカルメソッド領域(スレッドプライベート)

ローカル メソッド領域は Java スタックに似ていますが、違いは、仮想マシン スタックが Java メソッドの実行に使用されるのに対し、ローカル メソッド スタックはネイティブ メソッドに使用されることです。VM 実装が C リンケージ モデルを使用してネイティブ呼び出しをサポートする場合、この場合、スタックは A C スタックになりますが、HotSpot VM はローカル メソッド スタックと仮想マシン スタックを 1 つに直接結合します。

15. GC の実行を保証できますか?

いいえ、System.gc() または Runtime.gc() を呼び出すことはできますが、GC の実行を保証する方法はありません。

16. Java プログラムが使用するメモリを取得するにはどうすればよいですか? ヒープ使用率は %?

残りのメモリ、合計メモリ、および最大ヒープ メモリは、java.lang.Runtime クラスのメモリ関連メソッドを通じて取得できます。これらのメソッドを通じて、ヒープ使用率とヒープ メモリの残りの領域を取得することもできます。Runtime.freeMemory() メソッドは残りの領域のバイト数を返し、Runtime.totalMemory() メソッドは合計メモリのバイト数を返し、Runtime.maxMemory() メソッドは最大メモリのバイト数を返します。 。

17. Java におけるヒープとスタックの違いは何ですか?

JVM のヒープとスタックは異なるメモリ領域に属し、異なる目的に使用されます。スタックはメソッド フレームとローカル変数を保持するためによく使用されますが、オブジェクトは常にヒープ上に割り当てられます。スタックは通常、ヒープよりも小さく、複数のスレッド間で共有されませんが、ヒープは JVM 全体のすべてのスレッドによって共有されます。

18. JVM がクラス ファイルをロードする原理的なメカニズムを説明する

JVM でのクラスのロードは、クラス ローダー (ClassLoader) とそのサブクラスによって実装されます。Java のさまざまなローダーは、実行時にクラス ファイルを検索してロードする役割を担う Java ランタイム システムの重要なコンポーネントです。

Java のクロスプラットフォームの性質により、コンパイルされた Java ソース プログラムは実行可能プログラムではなく、1 つ以上のクラス ファイルになります。Java プログラムが特定のクラスを使用する必要がある場合、JVM はクラスがロード、接続 (検証、準備、解析) され、初期化されていることを確認します。クラスのロードとは、クラスの .class ファイル内のデータをメモリに読み取ることを指します。通常、バイト配列を作成して .class ファイルに読み取り、ロードされたクラスに対応する Class オブジェクトを生成します。

ロードが完了した後、Class オブジェクトは完成していないため、この時点ではクラスはまだ使用できません。クラスがロードされると、接続フェーズに入ります。これには、検証、準備 (静的変数にメモリを割り当て、デフォルトの初期値を設定する)、解決 (シンボリック参照を直接参照に置き換える) の 3 つのステップが含まれます。最後に、JVM は以下を含むクラスを初期化します: 1) クラスに直接の親クラスがあり、そのクラスが初期化されていない場合は、最初に親クラスを初期化します; 2) クラスに初期化ステートメントがある場合は、これらの初期化ステートメントを実行します順番通りに。

クラスのロードは、ルート ローダー (BootStrap)、拡張ローダー (Extension)、システム ローダー (System)、およびユーザー定義のクラス ローダー (java.lang.ClassLoader のサブクラス) を含むクラス ローダーによって実行されます。

Java 2 (JDK 1.2) 以降、クラスロードプロセスには親委任メカニズム (PDM) が採用されています。PDM は Java プラットフォームのセキュリティをより適切に保証します。このメカニズムでは、JVM に付属するブートストラップがルート ローダーであり、他のローダーには親クラス ローダーが 1 つだけあります。クラスのロードは、まず親クラスローダーにロードを要求し、親クラスローダーが無力な場合、その子クラスローダーが単独でロードします。JVM は、Java プログラムへのブートストラップへの参照を提供しません。以下はいくつかのクラスについてです

ローダーの説明:

(1) ブートストラップ: 通常はローカル コードで実装され、JVM 基本コア クラス ライブラリ (rt.jar) のロードを担当します。

(2) 拡張機能: java.ext.dirs システム プロパティで指定されたディレクトリからクラス ライブラリをロードします。その親ローダーはブートストラップです。

(3) システム: アプリケーション クラス ローダーとも呼ばれ、その親クラスは Extension です。最も広く使用されているクラスローダーです。環境変数のクラスパスまたはシステムプロパティから取得します

java.class.pathで指定されたディレクトリに記録されているクラスが、ユーザ定義ローダのデフォルトの親ローダとなります。

19. GCとは何ですか? なぜ GC があるのでしょうか?

GC とはガベージ コレクションのことです。メモリの処理はプログラマにとって問題が発生しやすい場所です。メモリの回復を忘れたり間違ったりすると、プログラムやシステムが不安定になったり、クラッシュしたりする可能性があります。Java が提供する GC 関数は、オブジェクトがスコープを超えているかどうかを自動的に監視できます。 Java 言語では、メモリを自動的に再利用するために、割り当てられたメモリを解放するための明示的な操作方法が提供されていません。Java プログラマーは、ガベージ コレクターがメモリ管理を自動的に処理するため、メモリ管理について心配する必要はありません。ガベージ コレクションを要求するには、 System.gc() または Runtime.getRuntime().gc() メソッドのいずれかを呼び出すことができますが、JVM は行外のガベージ コレクション呼び出しをマスクできます。

ガベージ コレクションはメモリ リークを効果的に防止し、利用可能なメモリを効果的に使用できます。ガベージ コレクターは通常、別個の優先度の低いスレッドとして実行されます。予期せぬ状況下では、メモリ ヒープ内で死んだオブジェクトや長期間使用されなかったオブジェクトはクリアされ、リサイクルされます。プログラマはガベージ コレクターをリアルタイムで呼び出すことができません。オブジェクトまたはすべてのオブジェクトがガベージ コレクションされます。Java 誕生の初期には、サーバー側プログラミングではメモリ リークを効果的に防ぐ必要があるため、ガベージ コレクションは Java の最大のハイライトの 1 つでしたが、時間の経過とともに、Java のガベージ コレクション メカニズムは批判されるようになりました。 。モバイルスマート端末のユーザーは通常、iOS システムの方が Android システムより優れたユーザー エクスペリエンスを持っていると感じていますが、その根深い理由の 1 つは、Android システムのガベージ コレクションが予測できないことです。

20. ヒープ (ヒープスレッド共有) - 実行時データ領域

スレッドが共有するメモリ領域で、作成したオブジェクトや配列はJavaヒープメモリに格納され、ガベージコレクタによるガベージコレクションにおいても最も重要なメモリ領域です。最新の VM は世代別コレクション アルゴリズムを使用しているため、Java ヒープは GC の観点から、新世代 (Eden エリア、From Survivor エリア、To Survivor エリア) と旧世代に再分割することもできます。

21. メソッド領域・永続生成(スレッド共有)

これは、JVM によってロードされたクラス情報、定数、静的変数、コンパイラによってコンパイルされたコードなどのデータを保存するために使用される、永続世代 (Permanent Generation) と呼ばれるものです。HotSpot VM は、GC 世代別コレクションをメソッドに拡張します。つまり、Java ヒープの永続生成を使用してメソッド領域を実装するため、HotSpot ガベージ コレクターは、メソッド領域用の特別なメモリ マネージャーを開発することなく、Java ヒープと同じようにメモリのこの部分を管理できます。 (永続的なメモリ回復の主な目的は、プールの定期的なリサイクルと型のアンロードであるため、一般に利益は小さいです)。

ランタイム定数プールはメソッド領域の一部です。Class ファイルには、クラスのバージョン、フィールド、メソッド、インターフェイス、その他の記述に加えて、コンパイル中に生成されるさまざまなリテラルやシンボル参照を格納するために使用される定数プール (定数プール テーブル) もあります。クラスがロードされた後、メソッド領域のランタイム定数プールに格納されます。Java仮想マシンではクラスファイル(当然定数プールも含む)の各部分の形式に厳しい規定があり、各バイトに格納されるデータは仕様の要件を満たしていなければ認識されません。仮想マシンをロードして実行します。

22. JVM ランタイムメモリ

GC の観点から見ると、Java ヒープは新世代 (Eden エリア、From Survivor エリア、To Survivor エリア) と旧世代にさらに分割することもできます。

23. 新しい世代

新しいオブジェクトを保存するために使用されます。通常、ヒープの 1/3 のスペースを占有します。オブジェクトが頻繁に作成されるため、新しい世代ではガベージ コレクションのために MinorGC が頻繁にトリガーされます。新しい世代は、Eden エリア、SurvivorFrom、SurvivorTo の 3 つのエリアに分かれています。

エデン地区

新しい Java オブジェクトの生成元 (新しく作成されたオブジェクトが大量のメモリを占有する場合、古い世代に直接割り当てられます)。Eden 領域のメモリが不足すると、MinorGC が起動され、新しい世代領域に対してガベージ コレクションが実行されます。

サーバーから

最後の GC の生き残りが、この GC のスキャン対象になります。

サーバーへ

MinorGC プロセスからの生存者は保持されます。

MinorGCの処理(コピー→クリア→スワップ)

MinorGC はレプリケーション アルゴリズムを使用します。

(1) eden、servitorFrom を ServicorTo にコピー、年齢 +1

まず、Eden 領域と SurvivorFrom 領域に残っているオブジェクトを ServicorTo 領域にコピーし(オブジェクトの年齢があり、古い年齢基準に達している場合は、古い世代領域に割り当てます)、同時に、これらのオブジェクトの年齢 (ServicorTo に十分なスペースがない場合は、高齢者エリアに配置します)。

(2)エンプティエデン、サービサーより

次に、Eden と ServicorFrom のオブジェクトをクリアします。

(3) ServicorTo と ServicorFrom 交換

最後に、ServicorTo と ServicorFrom が交換され、元の ServicorTo が次の GC の ServicorFrom 領域になります。

24. 旧世代

これは主に、アプリケーション内に長期間存続するメモリ オブジェクトを保存します。

旧世代のオブジェクトは比較的安定しているため、MajorGC が頻繁に実行されることはありません。通常、MinorGC は MajorGC の前に実行され、新しい世代のオブジェクトが古い世代に昇格され、十分なスペースがない場合にトリガーされます。新しく作成された大きなオブジェクトに割り当てるのに十分な大きさの連続した領域を見つけることができない場合、ガベージ コレクションで領域を確保するために事前に MajorGC がトリガーされます。

MajorGC はマーククリア アルゴリズムを使用します。最初にすべての古い時代を 1 回スキャンし、残っているオブジェクトをマークアウトしてから、マークされていないオブジェクトをリサイクルします。ajorGC はスキャンしてリサイクルする必要があるため、時間がかかります。MajorGC はメモリの断片化を生成するため、メモリの損失を減らすために、通常は次回に直接割り当てるためにメモリをマージするかマークアウトする必要があります。古い世代がいっぱいで収まらない場合、OOM (メモリ不足) 例外がスローされます。

25. 永久世代

メモリの永続的な記憶領域を指し、主にクラスやメタ(メタデータ)の情報が格納されます。クラスがロードされると永続領域に置かれます。インスタンスが格納される領域とは異なります。GCは行われません。メインプログラムの実行中に永続領域を変更します。クリーンアップされる領域。したがって、ロードされるクラスの数が増えると永続世代の領域がいっぱいになり、最終的には OOM 例外がスローされます。

26. JAVA8 とメタデータ

Java8 では、永続世代が削除され、「メタデータ領域」(メタスペース) と呼ばれる領域に置き換えられました。メタスペースの本質は永続世代の本質と似ていますが、メタスペースと永続世代の最大の違いは、メタスペースが仮想マシン内に存在せず、ローカル メモリを使用することです。したがって、デフォルトでは、メタスペースのサイズはローカル メモリによってのみ制限されます。クラスのメタデータはネイティブ メモリ、文字列プール、クラスの静的変数に配置され、Java ヒープに配置されるため、ロードできるクラス メタデータの量は MaxPermSize によって制御されなくなり、システムの実際の利用可能な領域によって制御されます。

27. 参照カウント

Java では、参照とオブジェクトが関連付けられます。オブジェクトを操作したい場合は、参照を使用する必要があります。したがって、オブジェクトが再利用可能かどうかを参照カウントによって判断するのが簡単な方法であることは明らかです。簡単に言えば、オブジェクトに関連付けられた参照がない場合、つまり参照カウントが 0 でない場合、これはオブジェクトが再度使用される可能性が低いことを意味し、このオブジェクトはリサイクル可能なオブジェクトです。

28. アクセシビリティ分析

参照カウント方式の循環参照問題を解決するために、Java では到達可能性分析の手法が使用されます。開始点として、一連の「GC ルート」オブジェクトを検索します。「GC ルート」とオブジェクトの間に到達可能なパスがない場合、オブジェクトは到達不能であると言われます。到達不可能なオブジェクトはリサイクル可能なオブジェクトと同等ではなく、到達不可能なオブジェクトがリサイクル可能なオブジェクトになるには少なくとも 2 つのマーキング プロセスが必要であることに注意してください。マークが 2 つ付いてもまだリサイクル可能なオブジェクトである場合は、リサイクルの対象となります。

29. マーク・スイープ・アルゴリズム(マーク・スイープ)

最も基本的なガベージ コレクション アルゴリズムは、マーキングとクリアの 2 つの段階に分かれています。マーキング フェーズでは、再利用する必要があるすべてのオブジェクトにマークを付け、クリーンアップ フェーズでは、マークされたオブジェクトが占めていたスペースを再利用します。写真のように

図から、このアルゴリズムの最大の問題はメモリの断片化が激しいことであり、将来的には大きなオブジェクトが空き領域を見つけられなくなるという問題が発生する可能性があることがわかります。

30. コピーアルゴリズム(コピー)

マークスイープアルゴリズムのメモリ断片化の欠陥を解決するために提案されたアルゴリズム。メモリ容量に応じてメモリを同じサイズの 2 つのブロックに分割します。毎回、そのうちの 1 つだけを使用してください。このメモリがいっぱいになったら、図に示すように、残っているオブジェクトを別のオブジェクトにコピーし、使用済みのメモリをクリアします。

このアルゴリズムは実装が簡単で、メモリ効率が高く、断片化が起こりにくいですが、最大の問題は、使用可能なメモリが元の半分に圧縮されてしまうことです。また、生き残るオブジェクトの数が増えると、コピー アルゴリズムの効率が大幅に低下します。

31. マークコンパクト

上記 2 つのアルゴリズムを組み合わせて、欠陥を回避するために提案されています。マーキング フェーズはマーク スイープ アルゴリズムと同じで、マーキング後、オブジェクトはクリーンアップされませんが、残ったオブジェクトはメモリの一端に移動されます。終了境界の外側にあるオブジェクトはクリアされます。図に示すように:

32. 世代別収集アルゴリズム

世代別コレクション方式は現在、ほとんどの JVM で採用されています。その中心的な考え方は、オブジェクトの異なるライフサイクルに従ってメモリを異なるドメインに分割することです。一般に、GC ヒープは古い世代 (Tenured/Old Generation) と古い世代 (Tenured/Old Generation) に分割されます。新しい世代(ヤングジェネレーション)。旧世代の特徴は、ガベージコレクションのたびにリサイクルする必要があるオブジェクトの数が少ないことですが、新世代の特徴は、ガベージコレクションのたびにリサイクルする必要があるオブジェクトの数が多いことです。 、地域に応じて異なるアルゴリズムを選択できます。

33. 新しい生成とレプリケーションのアルゴリズム

現在、ほとんどの JVM の GC では、新世代のコピー アルゴリズムが採用されています。これは、新世代の各ガベージ コレクションでほとんどのオブジェクトを回復する必要があるためです。つまり、コピーする操作が少なくなりますが、通常、新世代は1:1で分割されていません。一般に、新しい世代は、より大きな Eden スペースと 2 つの小さな Survivor スペース (From Space、To Space) に分割され、Eden スペースと Survivor スペースのいずれかが使用されるたびに、リサイクルするときに 2 つのスペースはオブジェクトになります。スペース内でまだ生きている場合は、別の Survivor スペースにコピーされます。

34. 旧世代とマーク複製アルゴリズム

古い世代では、毎回リサイクルされるオブジェクトの数が少ないため、Mark-Compact アルゴリズムが使用されます。

(1) JAVA 仮想マシンが言及するメソッド領域の Permanet Generation (Permanet Generation) は、クラス、定数、メソッドの説明などを格納するために使用されます。永続世代のコレクションには、主に廃止された定数と役に立たないクラスが含まれます。

(2) オブジェクトのメモリ割り当ては主に新世代の Eden スペースと Survivor スペースの From Space (Survivor が現在オブジェクトを格納している場所) にあり、場合によっては旧世代に直接割り当てられることもあります。

(3) 新世代の Eden Space と From Space が不足した場合は GC が発生し、GC 後に Eden Space と From Space 領域に残ったオブジェクトが To Space に移動され、さらに Eden Space と From に移動されます。空間がすっきりします。

(4) To Space でオブジェクトを十分に保存できない場合は、このオブジェクトを古い世代に保存します。

(5) GC 後、Eden Space と To Space が使用され、このサイクルが繰り返されます。

(6) Survivor でオブジェクトが 1 回 GC を脱出すると、オブジェクトの年齢が +1 されます。デフォルトでは、年齢が 15 に達したオブジェクトは古い世代に移動されます。

35. JAVA の強力な参照

Java で最も一般的なのは強参照であり、オブジェクトを参照変数に代入します。この参照変数は強参照です。オブジェクトが強参照変数によって参照されている場合、そのオブジェクトは到達可能な状態にあり、そのオブジェクトが今後 JVM によって使用されない場合でも、ガベージ コレクション メカニズムによってリサイクルすることはできません。したがって、強参照は Java メモリ リークの主な原因の 1 つです。

36. JAVAソフトリファレンス

ソフト参照は SoftReference クラスで実装する必要があります。ソフト参照のみのオブジェクトの場合、システム メモリが十分な場合はリサイクルされず、システム メモリが不足するとリサイクルされます。ソフト参照は、メモリに依存するプログラムでよく使用されます。

37. JAVAの弱参照

弱い参照は、ソフト参照よりも有効期間が短い WeakReference クラスを使用して実装する必要があります。弱い参照のみを持つオブジェクトの場合、ガベージ コレクション メカニズムが実行されている限り、JVM のメモリ領域が十分であるかどうかに関係なく、オブジェクトはオブジェクトによって占有されているメモリは常にリサイクルされます。

38. JAVA仮想リファレンス

ファントム参照は PhantomReference クラスによって実装する必要がありますが、単独で使用することはできず、参照キューと組み合わせて使用​​する必要があります。ファントム参照の主な目的は、ガベージ コレクションされているオブジェクトのステータスを追跡することです。

39. 世代別収集アルゴリズム

現在主流のVMガベージコレクションは、JVMにおける新世代、旧世代、永続世代といったオブジェクトのライフサイクルに応じてメモリを複数のブロックに分割する「Generational Collection」(ジェネレーションコレクション)アルゴリズムを採用している。各年代の特性に合わせて最適なGCアルゴリズムを使用可能

40. 新世代のレプリケーションアルゴリズムでは

すべてのガベージ コレクションでは、多数のオブジェクトが死んでいて、そのうちの少数のオブジェクトだけが生きていることがわかります。したがって、コピー アルゴリズムを選択すると、少数の生き残ったオブジェクトのコピー コストを支払うだけでコレクションを完了できます。オブジェクト

41. 旧世代 - マーク仕上げアルゴリズム

オブジェクトの生存率は高く、割り当てを保証するための余分なスペースがないため、「mark-clean」または「mark-organize」アルゴリズムを使用して、メモリをコピーせずにリサイクルし、メモリを直接解放する必要があります。

42. パーティション収集アルゴリズム

パーティション アルゴリズムは、ヒープ領域全体を異なる連続した小さな領域に分割し、それぞれが独立して使用され、独立してリサイクルされます。この利点は、一度にリサイクルする小さな領域の数を、目標の一時停止時間に応じて制御できることです。 、小さい領域を (ヒープ全体ではなく) 毎回適切な数の小さい領域を回復できるため、GC によって発生する一時停止が軽減されます。

43. GCガベージコレクター

Java ヒープ メモリは、新世代と旧世代の 2 つの部分に分かれています。新世代は主にコピーおよびマーク スイープ ガベージ コレクション アルゴリズムを使用し、旧世代は主にマーク ソート ガベージ コレクション アルゴリズムを使用します。各世代は、 JDK1.6 の Sun HotSpot 仮想マシンのガベージ コレクターは次のとおりです。

44. シリアルガベージコレクター(シングルスレッド、コピーアルゴリズム)

Serial (英語ではContinuous) は、コピー アルゴリズムを使用する最も基本的なガベージ コレクターであり、JDK1.3.1 より前の新世代の唯一のガベージ コレクターでした。Serial はシングルスレッド コレクターであり、ガベージ コレクション作業を完了するために 1 つの CPU または 1 つのスレッドを使用するだけでなく、ガベージ コレクションが終了するまで他のすべての作業スレッドを一時停止する必要があります。

シリアル ガベージ コレクターは、ガベージ コレクション プロセス中に他のすべてのワーカー スレッドを一時停止する必要がありますが、シンプルかつ効率的です。限られた単一 CPU 環境では、スレッド相互作用のオーバーヘッドがなく、最高のシングル スレッド ガベージ コレクション効率を得ることができます。したがって、Java 仮想マシンがクライアント モードで実行されている場合、シリアル ガベージ コレクタは依然としてデフォルトの新世代ガベージ コレクタです。

45. ParNew ガベージ コレクター (シリアル + マルチスレッド)

ParNew ガベージ コレクターは、実際にはシリアル コレクターのマルチスレッド バージョンであり、コピー アルゴリズムも使用します。ガベージ コレクションにマルチスレッドを使用することを除いて、残りの動作はシリアル コレクターとまったく同じです。ガベージ コレクターは、ガベージ コレクション プロセスでも同じことを行い、他のすべてのワーカー スレッドを一時停止します。

デフォルトでは、ParNew コレクターは CPU の数と同じ数のスレッドを開きます。ガベージ コレクターのスレッドの数は、-XX:ParallelGCThreads パラメーターによって制限できます。【パラレル:パラレル】

ParNew は、マルチスレッド化を除いてシリアル コレクターとほぼ同じですが、ParNew ガベージ コレクターは、サーバー モードで実行される新世代の多くの Java 仮想マシンのデフォルトのガベージ コレクターです。

46. パラレル スカベンジ コレクター (マルチスレッド コピー アルゴリズム、効率的)

Parallel Scavenge コレクタも新世代のガベージ コレクタです。また、コピー アルゴリズムを使用し、マルチスレッド ガベージ コレクタでもあります。制御可能なスループット (ただし、ユーザーの実行に CPU が使用される時間) を達成するプログラムに焦点を当てています。コード /CPU の合計消費時間、つまり、スループット = 実行中のユーザー コード時間/(実行中のユーザー コード時間 + ガベージ コレクション時間))、高スループットにより CPU 時間を最も効率的に使用でき、プログラムの計算タスクをすぐに完了できます。できるだけ多くの対話を必要とせずにバックグラウンドで動作するタスク。適応チューニング戦略も、ParallelScavenge コレクターと ParNew コレクターの重要な違いです。

47. Serial Old コレクター (シングルスレッド マークアップ アルゴリズム)

Serial Old は、Serial ガベージ コレクターの旧世代バージョンです。また、マークソート アルゴリズムを使用するシングルスレッド コレクターでもあります。このコレクターは、主にデフォルトのクライアントでも実行されます。

Java 仮想マシンのデフォルトの旧世代ガベージ コレクター。サーバー モードには、主に次の 2 つの目的があります。

(1) JDK1.5 より前のバージョンの新世代の Parallel Scavenge コレクターと組み合わせて使用​​されます。

(2) 旧世代の CMS コレクターを使用したバックアップ ガベージ コレクション方式として。新世代シリアルと旧世代シリアル オールドの組み合わせのガベージ コレクション プロセス図:

新世代の Parallel Scavenge コレクターの動作原理は、ParNew コレクターの動作原理と似ています。どちらもコピー アルゴリズムを使用するマルチスレッド コレクターであり、ガベージ コレクション プロセス中はすべてのワーカー スレッドを一時停止する必要があります。新世代の ParallelScavenge/ParNew と旧世代の Serial Old のガベージ コレクション プロセス図:

48. Parallel Old Collector (マルチスレッド マーキング アルゴリズム)

Parallel Old コレクターは、Parallel Scavenge の旧世代バージョンであり、JDK1.6 でのみ利用可能だったマルチスレッドのマークソート アルゴリズムを使用します。

JDK1.6より前では、新世代で使用されるParallelScavengeコレクタは、旧世代のSerial Oldコレクタとのみ併用可能であり、最初に新世代のスループットのみを保証できますが、全体のスループットは保証できません。また、スループット優先のガベージ コレクターも提供します。システムに高いスループット要件がある場合は、新世代の Parallel Scavenge と旧世代の Parallel Old コレクターのコロケーション戦略を優先できます。

新世代のパラレルScavengeと旧世代のパラレルオールドコレクターの動作プロセス図

49. CMS コレクター (マルチスレッド マーク スイープ アルゴリズム)

同時マーク スイープ (CMS) コレクターは、ガベージ コレクションの一時停止時間を最短にすることを主な目的とする旧世代のガベージ コレクターであり、他の旧世代のマーク ソート アルゴリズムとは異なり、マルチスレッド マーク クリア アルゴリズムを使用します。ガベージ コレクションの一時停止を最小限に抑えることで、対話性の高いプログラムのユーザー エクスペリエンスを向上させることができます。CMS の動作メカニズムは、他のガベージ コレクターのメカニズムよりも複雑です。プロセス全体は次の 4 つの段階に分かれています。

イニシャルマーク

GC ルートに直接関連付けることができるオブジェクトにマークを付けるだけです。これは非常に高速ですが、すべてのワーカー スレッドを一時停止する必要があります。

兼任マーク

GC ルート追跡のプロセスは、ワーカー スレッドを中断せずにユーザー スレッドで動作します。

ラベルを付け直す

同時マーキング中にユーザープログラムの継続実行によりマーキングが変化したオブジェクトの部分のマーキングレコードを修正するには、やはりすべてのワーカースレッドを一時停止する必要があります。

同時パージ

到達不能オブジェクトの GC ルートをクリアし、ワーカー スレッドを中断せずにユーザー スレッドを操作します。ガベージ コレクション スレッドは、最も時間がかかる同時マーキングおよび同時クリア プロセス中にユーザーと同時に動作できるため、一般に、CMS コレクターのメモリ回復とユーザー スレッドは同時に実行されます。CMSコレクターの作業プロセス

50.G1コレクター

Garbage の最初のガベージ コレクターは、ガベージ コレクター理論の開発における最も最先端の成果です。CMS コレクターと比較して、G1 コレクターの最も顕著な改善点は次の 2 つです。

(1) マーク照合アルゴリズムにより、メモリの断片化が発生しません。

(2) 休止時間を非常に正確に制御でき、スループットを犠牲にすることなく低休止ガベージ コレクションを実現できます。G1 コレクターは、全領域のガベージ コレクションを回避します。ヒープ メモリを固定サイズのいくつかの独立した領域に分割し、これらの領域でのガベージ コレクションの進行状況を追跡します。同時に、バックグラウンドで優先順位リストを維持します。 、許可された収集時間に従って、ゴミが最も多いエリアから最初に収集されます。G1 コレクターが限られた時間内で最高のガベージ収集効率を確実に達成できるようにするためのエリア分割と優先エリア リサイクル メカニズム

51. JVMクラスロードメカニズム

JVM クラスのロードの仕組みは、ロード、検証、準備、解析、初期化の 5 つの部分に分かれており、これら 5 つのプロセスを個別に見てみましょう。

ロード

ロードはクラスのロードプロセスの段階であり、この段階では、このクラスを表す java.lang.Class オブジェクトが、メソッド領域のこのクラスのさまざまなデータのエントリとしてメモリ内に生成されます。クラス ファイルから取得する必要はなく、ZIP パッケージ (jar パッケージや war パッケージなど) から読み取ることも、実行時に計算して生成することもできます (動的プロキシ)。他のファイル生成 (JSP ファイルを対応するクラス クラスに変換するなど) によって生成できます。

確認

この段階の主な目的は、クラス ファイルのバイト ストリームに含まれる情報が現在の仮想マシンの要件を満たしており、仮想マシン自体の安全性が危険にさらされないことを確認することです。

準備

準備段階とは、正式にクラス変数のメモリ確保とクラス変数の初期値の設定、つまりメソッド領域にクラス変数が使用するメモリ空間を確保する段階です。ここで述べた初期値の概念に注意してください。たとえば、クラス変数は次のように定義されます。

実際には、準備段階後の変数 v の初期値は 8080 ではなく 0 であり、プログラムのコンパイル後に v を 8080 に代入する put static 命令がクラスのコンストラクター メソッドに格納されます。

ただし、次のように宣言された場合に注意してください。

public static final int v = 8080;

ConstantValue 属性はコンパイル段階で v に対して生成され、仮想マシンは準備段階で ConstantValue 属性に従って v を 8080 に割り当てます。

分析する

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

public static int v = 8080;

実際には、準備段階後の変数 v の初期値は 8080 ではなく 0 であり、プログラムのコンパイル後に v を 8080 に代入する put static 命令がクラスのコンストラクター メソッドに格納されます。ただし、次のように宣言された場合に注意してください。

ConstantValue 属性はコンパイル段階で v に対して生成され、仮想マシンは準備段階で ConstantValue 属性に従って v を 8080 に割り当てます。

分析する

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

public static final int v = 8080;

ConstantValue 属性はコンパイル段階で v に対して生成され、仮想マシンは準備段階で ConstantValue 属性に従って v を 8080 に割り当てます。

分析する

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

(1) CONSTANT_Class_info

(2)CONSTANT_Field_info

(3)CONSTANT_メソッド情報

および他のタイプの定数。

シンボルリファレンス

シンボリック参照は仮想マシン実装のレイアウトとは何の関係もありません。また、参照されるターゲットをメモリにロードする必要は必ずしもありません。さまざまな仮想マシン実装のメモリ レイアウトは異なる場合がありますが、受け入れ可能なシンボリック参照は一貫している必要があります。これは、シンボリック参照のリテラル形式が Java 仮想マシン仕様のクラス ファイル形式で明確に定義されているためです。

直接見積もり

直接参照は、ターゲットへのポインタ、相対オフセット、またはターゲットを間接的に特定できるハンドルにすることができます。直接参照がある場合、参照されるターゲットはメモリ内にすでに存在している必要があります。

初期化

初期化フェーズはクラス ロードの最後のフェーズであり、前のクラス ロード フェーズの後は、ロード フェーズのカスタム クラス ローダーを除き、他の操作は JVM によって支配されます。初期段階では、クラスに定義されたJavaプログラムコードが実際に実行されます。

クラスコンストラクター

初期化フェーズは、クラス コンストラクター メソッドを実行するプロセスです。このメソッドは、コンパイラによって自動的に収集されたクラス内のクラス変数の代入操作と、静的ステートメント ブロック内のステートメントを組み合わせて形成されます。仮想マシンは、サブメソッドが実行される前に、親クラスのメソッドが実行されていることを保証します。クラス内に静的変数割り当てまたは静的ステートメント ブロックがない場合、コンパイラはこれに対して () メソッドを生成する必要はありません。クラス。次の状況ではクラスの初期化が実行されないことに注意してください。

(1) サブクラスを介して親クラスの静的フィールドを参照すると、親クラスの初期化のみがトリガーされ、サブクラスの初期化はトリガーされません。

(2) オブジェクトの配列を定義しても、このクラスの初期化はトリガーされません。

(3) 定数はコンパイル中に呼び出しクラスの定数プールに格納されますが、本質的には定数を定義するクラスへの直接参照はなく、定数が定義されているクラスをトリガーすることはありません。

(4) クラス名を通じて Class オブジェクトを取得しても、クラスの初期化はトリガーされません。

(5) Class.forName を通じて指定されたクラスをロードするとき、指定されたパラメータの初期化が false の場合、クラスの初期化はトリガーされません。実際、このパラメータは仮想マシンにクラスを初期化するかどうかを指示します。

(6) ClassLoader のデフォルトのloadClass メソッドでは、初期化アクションはトリガーされません。

52. クラスローダー

仮想マシン設計チームは、アプリケーションが必要なクラスを取得する方法を決定できるように、JVM の外部でロード アクションを実装します。JVM は、次の 3 種類のローダーを提供します。

ブートストラップ クラスローダー

JAVA_HOME\lib ディレクトリ、または -Xbootclasspath パラメータで指定されたパスにクラスをロードし、仮想マシンによって認識されます (rt.jar などのファイル名で識別されます)。

拡張クラスローダー

JAVA_HOME\lib\ext ディレクトリ、または java.ext.dirs システム変数で指定されたパスにクラス ライブラリをロードします。

アプリケーションクラスローダー:

ユーザー パス (クラスパス) でのクラス ライブラリのロードを担当します。JVM は親委任モデルを通じてクラスをロードします。もちろん、java.lang.ClassLoader を継承してカスタム クラス ローダーを実装することもできます。

53. 保護者代表団

クラスがクラス ロード リクエストを受信すると、最初にクラス自体をロードしようとするのではなく、リクエストを親クラスに委任して完了します。これはすべての階層クラス ローダーに当てはまります。そのため、すべてのロード リクエストは開始時に送信される必要があります。クラスのロードでは、親クラス ローダーがリクエストを完了できない (ロードされるクラスがロード パスに見つからない) と報告した場合にのみ、子クラス ローダーはそれを単独でロードしようとします。

親委任を使用する利点の 1 つは、たとえば、rt.jar パッケージにあるクラス java.lang.Object をロードするときに、どのローダーがこのクラスをロードしても、最終的には最上位の起動クラス ローダーに委託されることです。ロード用。これにより、異なるクラスローダーを使用しても、最終的には同じ Object オブジェクトが取得されるようになります。

54. OSGI(ダイナミックモデルシステム)

OSGi (Open Service Gateway Initiative) は、Java 指向の動的モデル システムであり、Java の動的モジュラー システムの一連の仕様です。

55. 構造を動的に変更する

OSGi サービス プラットフォームは、再起動せずにさまざまなネットワーク デバイスの構成を動的に変更する機能を提供します。結合を最小限に抑え、これらの結合を管理しやすくするために、OSGi テクノロジは、これらのコンポーネントが動的に相互に検出できるようにするサービス指向アーキテクチャを提供します。

56. モジュール式プログラミングとホットスワップ

OSGi は、Java プログラムのモジュール プログラミングの基本条件を提供することを目的としています。OSGi ベースのプログラムは、モジュール レベルのホットスワップ機能を実装できる可能性があります。プログラムが更新されると、プログラムの一部のみを無効にしたり、再インストールしたり、再インストールしたりすることができます。これは、エンタープライズ プログラム開発にとって非常に魅力的な機能です。

OSGi は、非常に優れたモジュール開発目標を記述し、この目標を達成するために必要なサービスとアーキテクチャを定義し、実装サポートのための成熟したフレームワークも備えています。OSGi は強力な機能を提供しますが、クラス読み込みの親委任モデルに準拠していないため、さらなる複雑さも生じます。

57. JVMメモリモデル

スレッド専用: スタック、ネイティブ メソッド スタック、プログラム カウンタ

スレッド共有: ヒープ、メソッド領域

58. スタック

メソッド スタックとも呼ばれ、スレッドに対してプライベートなスレッド実行メソッドは、ローカル変数テーブル、操作スタック、ダイナミック リンク、メソッド出口などの情報を保存するスタック配列を作成します。メソッドが呼び出されるとき、 、スタック上で実行され、メソッドはスタックに戻ります。

59. ネイティブメソッドスタック

スタックと同様に実行メソッドの情報を保存するためにも使用され、Javaメソッドの実行にはスタックが使用され、ネイティブメソッドの実行時にはネイティブメソッドのスタックが使用されます。

60. プログラムカウンター

現在のスレッドによって実行されたバイトコードの位置を保存します。各スレッドの動作時には独立したカウンタがあり、Java メソッドの実行のみに使用されます。ネイティブ メソッドが実行されるとき、プログラム カウンタは空です。

61. ヒープ

JVM メモリ管理の最大の部分はスレッドによって共有されます。その目的は、オブジェクト インスタンスを保存することです。必要なオブジェクト インスタンスのほぼすべてがここに配置されます。ヒープに利用可能なスペースがない場合、OOM 例外がスローされます。オブジェクトのライフサイクル。JVM はオブジェクトを世代ごとに管理し、ガベージ コレクターはガベージ コレクションの管理を実行します。

62.メソッドエリア

非ヒープ領域とも呼ばれ、仮想マシンにロードされたクラス情報、定数、静的変数、リアルタイム コンパイラによって最適化されたコードなどのデータを格納するために使用されます。 1.8 のメタスペースは両方ともメソッド area の実装です。

63. 世代間のリサイクル

世代別リサイクルは 2 つの事実に基づいています。ほとんどのオブジェクトはすぐに使用されなくなり、一部のオブジェクトはすぐに役に立たなくなるわけではありませんが、長期間使用できるわけではありません。

若い世代 -> マークコピー

旧世代 -> マーククリア

64. ヒープとスタックの違い

スタックは、ロジックを表す実行単位であり、ヒープ内に基本データ型とオブジェクト参照が含まれ、領域は断片化せずに連続しています。ヒープは、複数のスタックで共有できるデータを表す記憶単位(基本データを含む)です。メンバー、参照、および参照オブジェクトの型)、それらが配置されている領域は不連続であり、断片が存在します。

(1) 機能の違い

スタック メモリはローカル変数とメソッド呼び出しの保存に使用され、ヒープ メモリは Java のオブジェクトの保存に使用されます。メンバー変数、ローカル変数、クラス変数のいずれであっても、それらが指すオブジェクトはヒープ メモリに格納されます。

(2) 異なる共有

スタックメモリはスレッドプライベートです。

ヒープ メモリはすべてのスレッドで共有されます。

(3) 異常エラーは別

スタックメモリまたはヒープメモリが不足している場合は、例外がスローされます。

スタック領域が不十分です: java.lang.StackOverFlowError。

ヒープ領域が不十分です: java.lang.OutOfMemoryError。

(4) スペースサイズ

スタックのサイズはヒープのサイズよりもはるかに小さい

65. FullGC はいつトリガーされますか?

System.gc を直接呼び出す以外に、次の 4 つの状況で Full GC の実行がトリガーされます。

(1) 旧世代では容量不足

新世代のオブジェクトを転送してラージオブジェクトやラージ配列として作成する場合のみ旧世代の領域が不足し、Full GC実行後もまだ領域が不足している場合は以下のエラーがスローされます

エラー:

java.lang.OutOfMemoryError: Java ヒープ スペース

上記 2 つの状況によって引き起こされる FullGC を回避するには、チューニング時にオブジェクトをマイナー GC フェーズでリサイクルし、オブジェクトを新しい世代でより長期間存続させ、大きすぎるファイルを作成しないようにしてください。オブジェクトと配列。

(2) Permanet Generation スペースがいっぱいです

PermanetGeneration はクラス情報などを格納します。システム内にロードするクラス、反映されるクラス、呼び出すメソッドが多数ある場合、Permanet Generation がいっぱいになることがあります。CMS GC を使用するように設定されていない場合は、Full GC が実行されます。フル GC 後もリサイクルできない場合、JVM は次のエラー メッセージをスローします。

java.lang.OutOfMemoryError: PermGen スペース

フル Perm Gen によって引き起こされる Full GC 現象を回避するには、Perm Gen のスペースを増やすか、CMS GC に切り替えることが考えられます。

(3) CMS GC 中にプロモーションが失敗し、同時モード障害が発生しました。

旧世代の GC に CMS を使用するプログラムの場合、GC ログにプロモーション失敗とコンカレント モード失敗があるかどうかに特に注意してください。これら 2 つの条件が発生すると、フル GC がトリガーされる可能性があります。

Promotionfailed は、マイナー GC 中に Survivor スペースを配置できず、オブジェクトは古い世代にのみ配置でき、現時点では古い世代を配置できないという事実によって発生します。並行モードの失敗は、オブジェクトが配置されることによって引き起こされます。 CMS GC を同時に実行する処理と古い世代、このとき古い世代は容量不足が原因です。

対策としては、survivorspace や古い世代の領域を増やすか、同時 GC をトリガーする割合を減らすことが挙げられますが、JDK 5.0 以降および 6.0 以降のバージョンでは、JDK のせいでリマークが完了してからかなり時間が経ってから CMS がスイープ アクションをトリガーする可能性があります。バグ29 。この状況は、-XX:CMSMaxAbortablePrecleanTime=5 (ミリ秒) を設定することで回避できます。

(4) 旧世代に昇格したマイナー GC の平均サイズが、旧世代の残りの領域よりも大きい

これは比較的複雑なトリガー状況であり、新世代のオブジェクトが旧世代に昇格することで旧世代の領域が不足する現象を避けるため、マイナー GC を実行する際に Hotspot が判断を行います。以前の統計 古い世代の平均サイズが古い世代の残りの領域より大きい場合、フル GC が直接トリガーされます。

たとえば、プログラムが初めて MinorGC をトリガーした後、6MB のオブジェクトが古い世代に昇格され、次のマイナー GC が発生したときに、まず古い世代の残りの領域が 6MB より大きいかどうか、およびそれより小さいかどうかを確認します。 6MB を超える場合は、Full GC を実行します。

新しい世代が PSGC を採用する場合、方法が少し異なります。PS GC は、マイナー GC の後にもチェックします。たとえば、上記の例では、最初のマイナー GC の後、PS GC は、旧世代の残りのスペースが 2 より大きいかどうかをチェックします。現時点では 6MB ですが、それ未満の場合は古い世代のリサイクルがトリガーされます。上記の 4 つの状況に加えて、RPC または管理に RMI を使用する Sun JDK アプリケーションの場合、デフォルトではフル GC が 1 時間に 1 回実行されます。起動時に -java-Dsun.rmi.dgc.client.gcInterval=3600000 を渡すことでフル GC の実行間隔を設定するか、-XX:+ DisableExplicitGC を渡すことで RMI による System.gc の呼び出しを禁止できます。

66. Java仮想マシンとは何ですか? Java が「プラットフォームに依存しないプログラミング言語」と呼ばれるのはなぜですか?

Java 仮想マシンは、Java バイトコードを実行できる仮想マシン プロセスです。Java ソース ファイルは、Java 仮想マシンで実行できるバイトコード ファイルにコンパイルされます。Java は、プログラマーがプラットフォームごとに個別に書き直したり再コンパイルしたりする必要がなく、アプリケーションをあらゆるプラットフォームで実行できるように設計されています。Java 仮想マシンは、基礎となるハードウェア プラットフォームの命令長やその他の特性を認識しているため、これが可能になります。

67. オブジェクト割り当てルール

(1) オブジェクトは最初に Eden 領域に配置されますが、Eden 領域に十分なスペースがない場合、仮想マシンはマイナー GC を実行します。

(2) 大きなオブジェクトはそのまま古い時代に突入します (大きなオブジェクトとは、大量の連続メモリ空間を必要とするオブジェクトを指します)。この目的は、Eden 領域と 2 つの Survivor 領域の間で大量のメモリ コピーが行われないようにすることです (新世代ではメモリを収集するためにコピー アルゴリズムが使用されます)。

(3) 寿命の長いものは老朽化します。仮想マシンは各オブジェクトの経過時間カウンターを定義します。オブジェクトが一度マイナー GC を通過すると、オブジェクトは Survivor 領域に入ります。それ以降、オブジェクトの経過時間はマイナー GC を通過するたびに 1 ずつ増加します。しきい値に達すると、オブジェクトは老年領域に入ります。

(4) オブジェクトの年齢を動的に判断します。Survivor エリア内の同じ年齢のすべてのオブジェクトのサイズの合計が Survivor スペースの半分より大きい場合、この年齢以上の年齢を持つオブジェクトは直接古い年齢に入ることができます。

(5) スペース割り当ての保証。マイナー GC が実行されるたびに、JVM は Survivor 領域から古い領域に移動されたオブジェクトの平均サイズを計算します。この値が古い領域に残っている値のサイズより大きい場合、フル GC が実行されます。値未満の場合は、HandlePromotionFailure の設定を確認します。true の場合は監視のみを実行します。false の場合はフル GC を実行します。

68. JVM がクラス ファイルをロードする原理的なメカニズムについて説明してください。

JVM でのクラスのロードは、クラス ローダー (ClassLoader) とそのサブクラスによって実装されます。Java のクラス ローダーは、実行時にクラス ファイルを検索してロードする重要な Java ランタイム システム コンポーネントです。

Java のクロスプラットフォームの性質により、コンパイルされた Java ソース プログラムは実行可能プログラムではなく、1 つ以上のクラス ファイルになります。Java プログラムが特定のクラスを使用する必要がある場合、JVM はクラスがロード、接続 (検証、準備、解析) され、初期化されていることを確認します。

クラスのロードとは、クラスの .class ファイル内のデータをメモリに読み取ることを指します。通常、バイト配列を作成して .class ファイルに読み取り、ロードされたクラスに対応する Class オブジェクトを生成します。ロードが完了した後、Class オブジェクトは完成していないため、この時点ではクラスはまだ使用できません。

クラスがロードされると、接続フェーズに入ります。これには、検証、準備 (静的変数にメモリを割り当て、デフォルトの初期値を設定する)、解決 (シンボリック参照を直接参照に置き換える) の 3 つのステップが含まれます。最後に、JVM はクラスを初期化します。

含む:

(1) クラスに直接の親クラスがあり、そのクラスが初期化されていない場合は、最初に親クラスを初期化します。

(2) クラス内に初期化文がある場合は、それらの初期化文を順番に実行します。クラスのロードは、ルート ローダー (BootStrap)、拡張ローダー (Extension)、システム ローダー (System)、およびユーザー定義のクラス ローダー (java.lang.ClassLoader のサブクラス) を含むクラス ローダーによって実行されます。

Java 2 (JDK 1.2) 以降、クラスロードプロセスには親委任メカニズム (PDM) が採用されています。PDM は Java プラットフォームのセキュリティをより適切に保証します。このメカニズムでは、JVM に付属するブートストラップがルート ローダーであり、他のローダーには親クラス ローダーが 1 つだけあります。クラスのロードは、まず親クラスローダーにロードを要求し、親クラスローダーが無力な場合、その子クラスローダーが単独でロードします。JVM は、Java プログラムへのブートストラップへの参照を提供しません。以下は、いくつかのクラスローダーの説明です。

ブートストラップ: 通常はローカル コードで実装され、JVM 基本コア クラス ライブラリ (rt.jar) のロードを担当します。

拡張機能: java.ext.dirs システム プロパティで指定されたディレクトリからクラス ライブラリをロードします。その親ローダーは Bootstrap です。

システム: アプリケーション クラス ローダーとも呼ばれ、その親クラスは Extension です。最も広く使用されているクラスローダーです。これは、環境変数 classpath またはシステム プロパティ java.class.path で指定されたディレクトリからクラスを記録し、ユーザー定義ローダーのデフォルトの親ローダーです。

69. Javaオブジェクト作成プロセス

(1) JVM が新しいオブジェクトを作成する命令を検出すると、まず、この命令のパラメータが定数プール内のクラスへのシンボリック参照を定義できるかどうかを確認します。次に、このクラスをロードします (クラスのロードプロセスについては後で説明します)。

(2) オブジェクトにメモリを割り当てます。1 つの方法は「ポインター衝突」、もう 1 つの方法は「フリー リスト」、そして最後に一般的に使用される方法は「ローカル スレッド バッファ割り当て (TLAB)」です。

(3) オブジェクトヘッダを除くオブジェクトメモリ空間を0に初期化する

(4) オブジェクトヘッダーに必要な設定を行う

70. Javaのオブジェクト構造を簡単に説明します

Java オブジェクトは、オブジェクト ヘッダー、インスタンス データ、および位置合わせパディングの 3 つの部分で構成されます。

オブジェクト ヘッダーは 2 つの部分で構成され、最初の部分には、オブジェクト自体の実行時データ (ハッシュ コード、GC 生成経過時間、ロック識別ステータス、スレッドが保持するロック、バイアスされたスレッド ID (通常は 32/64 ビットを占有する)) が格納されます。2 番目の部分はポインター型で、オブジェクトのクラス メタデータ型 (つまり、オブジェクトがどのクラスを表すか) を指します。配列オブジェクトの場合、オブジェクト ヘッダーの別の部分を使用して配列の長さを記録します。

インスタンス データは、オブジェクトの実際の有効な情報 (親クラスから継承されたものや独自に定義されたものを含む) を格納するために使用されます。

アライメント充填: JVM では、オブジェクトの開始アドレスが 8 バイトの整数倍である必要があります (8 バイト アライメント)。

71. リサイクル可能かどうかの判断方法

オブジェクトが生きているかどうかを判断するには、通常、次の 2 つの方法があります。

参照カウント: 各オブジェクトは参照カウント属性を持ち、参照が追加されるとカウントが 1 増加し、参照が解放されるとカウントが 1 減少します。カウントが 0 の場合は再利用できます。この方法は単純ですが、オブジェクト間の循環参照の問題を解決できません。

到達可能性分析: GC ルートから開始して下方向に検索します。検索によって移動するパスは参照チェーンと呼ばれます。オブジェクトに GC ルートに接続された参照チェーンがない場合、そのオブジェクトは利用できず、到達できないことがわかります。

72. JVM の永続世代ではガベージ コレクションが発生しますか?

永続世代ではガベージ コレクションは発生しません。永続世代がいっぱいであるか、しきい値を超えると、フル ガベージ コレクション (フル GC) がトリガーされます。ガベージ コレクターの出力を注意深く見ると、永続世代も収集されていることがわかります。このため、フル GC を回避するには、正しい永続世代サイズが非常に重要です。「Java8: 永続世代からメタデータ領域へ」を参照してください (注: Java8 では永続世代が削除され、メタデータ領域と呼ばれる新しいネイティブ メモリ領域が追加されました)

73. ガベージコレクションアルゴリズム

GC には、マーク スイープ アルゴリズム、コピー アルゴリズム、マーク圧縮アルゴリズムの 3 つの基本アルゴリズムがあり、一般的に使用されるガベージ コレクターでは、通常、世代別コレクション アルゴリズムが使用されます。

マークスイープアルゴリズム

「マーク-スイープ」アルゴリズムは、その名前のように、タグ付きオブジェクトの「マーク」と「スイープ」の 2 つの段階に分かれています。

コピーアルゴリズム

「コピー」収集アルゴリズム。使用可能なメモリを容量に応じて同じサイズの 2 つの部分に分割し、一度に 1 つだけを使用します。このブロックのメモリがなくなったら、残ったオブジェクトを別のブロックにコピーし、使用済みのメモリ空間を一度にクリーンアップします。

マーク圧縮アルゴリズム

マーキングプロセスは依然として「マーククリア」アルゴリズムと同じですが、後続のステップではリサイクル可能なオブジェクトを直接クリーンアップするのではなく、生き残ったすべてのオブジェクトを一方の端に移動させ、その後、端の境界の外側にあるメモリを直接クリーンアップします。

世代別収集アルゴリズム

「世代別収集」アルゴリズムは、Javaヒープを新世代と旧世代に分割し、各時代の特性に応じて最適な収集アルゴリズムを採用します。

74. チューニングコマンドとは何ですか?

Sun JDK の監視および障害処理コマンドには、jps jstat jmap jhat jstack jinfo が含まれます

(1) jps (JVM プロセス ステータス ツール) は、指定されたシステム内のすべての HotSpot 仮想マシン プロセスを表示します。

(1) jstat、JVM 統計監視 は、仮想マシンの実行中のステータス情報を監視するコマンドで、仮想マシンのプロセスにおけるクラスローディング、メモリ、ガベージコレクション、JIT コンパイルなどの実行データを表示できます。 。

(3) jmap、JVM メモリ マップ コマンドはヒープ ダンプ ファイルの生成に使用されます。

(4) jhat、JVM ヒープ分析ツール コマンドは、jmap によって生成されたダンプを分析するために jmap と組み合わせて使用​​されます。jhat には小型 HTTP/HTML サーバーが組み込まれています。ダンプ分析結果を生成した後、ブラウザ

(5) jstack。現時点での Java 仮想マシンのスレッド スナップショットを生成するために使用されます。

(6) jinfo、JVM 構成情報 このコマンドは、仮想マシンの動作パラメータをリアルタイムで表示および調整するために使用されます。

75. チューニングツール

一般的に使用されるチューニング ツールは 2 つのカテゴリに分類されます。jdk には jconsole と jvisualvm という監視ツールが付属しており、サードパーティには MAT (メモリ アナライザー ツール)、GChisto が含まれます。

(1) jconsole、Java 監視および管理コンソールは、java5 以降の JDK に付属する Java 監視および管理コンソールであり、JVM のメモリ、スレッド、およびクラスを監視するために使用されます。

(2) jvisualvm、jdk には、メモリ スナップショットとスレッド スナップショットを分析し、メモリ変更や GC 変更などを監視できる多用途ツールが付属しています。

(3) Eclipse ベースのメモリ分析ツールである MAT (Memory Analyzer Tool) は、高速で機能が豊富な Java ヒープ分析ツールであり、メモリ リークを見つけてメモリ消費量を削減するのに役立ちます。

(4) GChisto、GC ログを分析するための専門ツール

76. マイナー GC とフル GC はそれぞれいつ発生しますか?

MGC は YGC とも呼ばれ、新しい世代のメモリが不足している場合に発生し、FGC は JVM のメモリが不足している場合に発生します。

77. JVM パフォーマンス チューニングについて何を知っていますか

ヒープメモリサイズを設定する

-Xmx: 最大ヒープ メモリ制限。

若い世代のサイズを設定します。新しい世代は小さすぎてはなりません。小さすぎると、多数のオブジェクトが古い世代に溢れてしまいます。

-XX:NewSize: 新しい世代のサイズ

-XX:NewRatio 新世代と旧世代の割合

-XX:SurvivorRatio: エデン空間と生存者空間の比率

ガベージ コレクターを設定します。若い世代 -XX:+UseParNewGC 古い世代 -XX:+UseConcMarkSoupGC

やっと

公式アカウントにご注目ください: 風を追うプログラマー、003 に返信すると、最新の 2020 Java 面接質問マニュアル (200 ページを超える PDF 概要) が届きます。

 

おすすめ

転載: blog.csdn.net/Design407/article/details/106874914
おすすめ