序文
- jvm構造システム
すべてのJavaプログラムはJava仮想マシンから分離できず、Javaプログラムの実行は特定のJava仮想マシンインスタンスに依存しています。Java仮想マシン仕様では、これらの用語は、サブシステム、メモリ領域、データタイプ、および命令を説明するために使用されます。一緒に、これらのコンポーネントは、抽象的な仮想マシン内の抽象的なアーキテクチャを示しています。
Java仮想マシンは、主に5つのモジュールに分かれています。クラスローダーサブシステム、ランタイムデータ領域、実行エンジン、ローカルメソッドインターフェイス、およびガベージコレクションモジュールです。その中で、ガベージコレクションモジュールは、Java仮想マシン仕様のJava仮想マシンのガベージコレクションを必要としませんが、無制限のメモリが発明される前は、ほとんどのJVM実装にガベージコレクションがありました。ランタイムデータ領域は、すべてのJAVA仮想マシンインスタンスに何らかの形で存在しますが、Java仮想マシン仕様でのその説明は非常に抽象的です。これらのランタイムデータ構造の詳細は、ほとんどの場合、特定の実装の設計者によって決定されます。
この記事はスペースに限りがあり、共有するために私のメモから一部だけを切り取っています。JVMやその他の実際のインタビューの質問(詳細な分析と質問への回答を含む)についての知識が必要な場合は
、ここをクリックしてください。 qf
1.スタックメモリのオーバーフローはどのような状況で発生しますか?
(1)考える
スタック定義を説明し、次にオーバーフローする理由を説明してから、関連する構成パラメーターを説明します。OKの場合は、スタックオーバーフローのデモをインタビュアーに書き込むことができます。
(2)私の答え
スタックはスレッド専用であり、そのライフサイクルはスレッドのライフサイクルと同じです。各メソッドが実行されると、ローカル変数テーブル、オペランドスタック、動的リンク、およびメソッド出口ランプ情報を格納するスタックフレームが作成されます。ローカル変数テーブルには、基本的なデータタイプ、オブジェクト参照タイプも含まれています
スレッドによって要求されたスタックの深さが仮想マシンによって許可された最大の深さよりも大きい場合、StackOverflowError例外がスローされ、メソッドの再帰呼び出しによってこの結果が生成されます。
拡張を完了するのに十分なメモリを適用できない場合、または新しいスレッドを作成するときに対応する仮想マシンスタックを作成するのに十分なメモリがない場合、java仮想マシンはOutOfMemoryError例外をスローします。(開始されたスレッドが多すぎます)
JVMスタックのサイズを調整するためのパラメーター-Xss
2.詳細なJVMメモリモデル
(1)考える
インタビュアーのJVMメモリモデル図を作成し、各モジュールの定義と機能、およびスタックオーバーフローなどの考えられる問題について説明します。
(2)私の答え
JVMメモリ構造
- プログラムカウンター:現在のスレッドによって実行されているバイトコードの行番号インジケーター。実行されている仮想マシンのバイト命令アドレスを記録するために使用され、スレッドはプライベートです。
- Java仮想スタック:基本的なデータタイプ、オブジェクト参照、メソッド出口などを格納し、スレッドをプライベートにします。
- ネイティブメソッドスタック:ネイティブメソッドを提供し、スレッド専用であることを除いて、仮想スタックに似ています。
- Javaヒープ:Javaメモリの最大の部分であるすべてのオブジェクトインスタンスと配列は、GCが収集されてスレッドによって共有されるjavaヒープに格納されます。
- メソッド領域:ロードされたクラス情報、定数、静的変数、およびジャストインタイムコンパイラによってコンパイルされたコードデータを格納します。(つまり、永久ベルト)、リサイクルの主な目標は、各スレッドで共有される、一定のプールのリサイクルとタイプのアンロードです
3.なぜJVMメモリを新世代、旧世代、永続世代に分割する必要があるのですか?新世代がエデンとサバイバーに分かれているのはなぜですか?
(1)考える
新しい世代の分割であるJAVAヒープについて説明し、次にそれらの間の変換、それらの間のいくつかのパラメーターの構成(-XX:NewRatio、-XX:SurvivorRatioなど)について説明し、次にそれがこのように分割される理由を説明します。少し理解を加えたほうがいいです。
(2)私の答え
1)共有メモリ領域の分割
- 共有メモリ領域=永続ゾーン+ヒープ
- 永続ゾーン=メソッド領域+その他
- Javaヒープ=旧世代+新世代
- 若い世代=エデン+ S0 + S1
2)いくつかのパラメーターの構成
- デフォルトでは、若い世代(Young)と古い世代(Old)の比率は1:2であり、パラメーター-XX:NewRatioを使用して構成できます。
- デフォルトでは、Edem:from:to = 8:1:1(パラメーター-XX:SurvivorRatioで設定できます)
サバイバー領域のオブジェクトがコピーされる回数は15回です(仮想マシンパラメーター-XX:+ MaxTenuringThresholdに対応)
3)なぜエデンとサバイバーに分かれているのですか?なぜサバイバーエリアが2つあるのですか?
サバイバーがいない場合、エデンエリアでマイナーGCが実行されるたびに、サバイバーオブジェクトが古い世代に送信されます。古い世代はすぐにいっぱいになり、メジャーGCがトリガーされます。古い世代のメモリスペースは新しい世代のメモリスペースよりもはるかに大きく、フルGCの実行にはマイナーGCよりもはるかに時間がかかるため、エデンとサバイバーに分割する必要があります。
サバイバーの重要性は、旧世代に送信されるオブジェクトを減らし、それによってフルGCの発生を減らすことです。サバイバーの事前スクリーニングにより、16個のマイナーGCの後に若い世代で生き残ることができるオブジェクトのみが旧世代に送信されることが保証されます。年。
2つのサバイバーエリアを設定する最大の利点は、断片化を解決することです。エデンで新しく作成されたオブジェクトはマイナーGCを受け、エデンでサバイバーオブジェクトは最初のサバイバースペースS0に移動され、エデンは空になります。エデンを待ちます。エリアが再びいっぱいになると、マイナーGCが再度トリガーされ、EdenとS0の存続オブジェクトがコピーされ、2番目の存続スペースS1に送信されます(このコピーアルゴリズムにより、S1のS0とEdenが保証されるため、このプロセスは非常に重要です。 2つの部分からなるライブオブジェクトは、断片化を回避するために連続したメモリスペースを占有します)
4. JVMの完全なGCプロセスとは何ですか?被験者はどのように老年期に昇進しますか?
(1)考える
最初にJavaヒープメモリの分割について説明し、次にマイナーGC、メジャーGC、およびフルGCについて説明し、それらの間の変換プロセスについて説明します。
(2)私の答え
Javaヒープ=旧世代+新世代
若い世代=エデン+ S0 + S1
エデンエリアのスペースがいっぱいになると、Java仮想マシンがマイナーGCをトリガーして新世代のゴミを収集し、生き残ったオブジェクトがサバイバーエリアに転送されます。
大きなオブジェクト(非常に長い文字列など、大量の連続したメモリスペースを必要とするJavaオブジェクト)は、直接古い状態になります。
対象がエデンで生まれ、最初のマイナーGCを生き延び、サバイバーが収容している場合、年齢は1に設定され、マイナーGCごとに年齢は+1になります。年齢が一定の制限(15)を超えると、老後への昇進。つまり、長期間存続するオブジェクトは古い状態になります。
古い世代はいっぱいで、それ以上のオブジェクトを収容できません。マイナーGCの後、通常、フルGCが実行されます。フルGCは、若い世代と古い世代を含むメモリヒープ全体をクリーンアップします。
メジャーGCは古い時代のGCで発生します。古いエリアのクリーンアップには、マイナーGCよりも10倍以上遅いマイナーGCが少なくとも1つ伴うことがよくあります。
5.原則、プロセス、長所と短所を含め、どの種類のガベージコレクター、それらの長所と短所、cmsとG1に焦点を当てているか知っていますか
(1)考える
典型的なガベージコレクター、特にcmsとG1、それらの原理と違い、および関連するガベージコレクションアルゴリズムを忘れないでください。
(2)私の答え
1)いくつかのガベージコレクター
- シリアルコレクター:シングルスレッドコレクター。ガベージを収集するときは、ワールドを停止してコピーアルゴリズムを使用する必要があります。
- ParNewコレクター:シリアルコレクターのマルチスレッドバージョンも、ワールドを停止してアルゴリズムをコピーする必要があります。
- Parallel Scavengeコレクター:新世代コレクター、レプリケーションアルゴリズムのコレクター、同時マルチスレッドコレクター、目標は制御可能なスループットを達成することです。仮想マシンが合計100分間実行され、そのうちガベージが1分かかる場合、スループットは99%です。
- シリアルオールドコレクター:これは、タグソートアルゴリズムを使用するシングルスレッドコレクターであるシリアルコレクターの古いバージョンです。
- Parallel Old Collector:これは、マルチスレッド、マークソートアルゴリズムを使用するParallel ScavengeCollectorの古いバージョンです。
- CMS(Concurrent Mark Sweep)コレクター:最短のリカバリー休止時間を取得することを目的としたコレクターです。マーク除去アルゴリズム、操作プロセス:初期マーキング、同時マーキング、リマーキング、同時除去、コレクションの終了により、多くのスペースデブリが生成されます。
- G1コレクター:マークソートアルゴリズムの実装。操作プロセスには、主に次のものが含まれます:初期マーク、同時マーク、最終マーク、スクリーニングマーク。スペースデブリは発生せず、一時停止を正確に制御できます。
2)CMSコレクターとG1コレクターの違い
CMSコレクターは旧世代のコレクターであり、新世代のSerialおよびParNewコレクターで使用できます。
G1コレクターの収集範囲は古い世代と若い世代であり、他のコレクターと組み合わせて使用する必要はありません。
CMSコレクターは、一時停止時間を最小限に抑えることを目的としたコレクターです。
G1コレクターはガベージコレクションの一時停止時間を予測できます
CMSコレクターは、メモリの断片化が発生しやすいガベージコレクションに「マークスイープ」アルゴリズムを使用します
G1コレクターは、「マーク定義」アルゴリズムを使用してスペースを統合し、メモリスペースの断片化を減らします。
6.並べ替え、メモリバリア、発生前、メインメモリ、作業メモリなど、JVMメモリモデルの関連知識についてどの程度知っていますか。
(1)考える
最初に、揮発性の例と組み合わせたJavaメモリモデルの図を描いて、並べ替えとメモリバリアとは何かを説明します。次のデモ手順をインタビュアーに書くのが最善です。
(2)私の答え
1)Javaメモリモデル図:
Javaメモリモデルでは、すべての変数がメインメモリに格納されることが規定されています。各スレッドには独自の作業メモリがあります。スレッドの作業メモリには、スレッドで使用される変数のメインメモリのコピーが格納されます。すべての操作は作業メモリーで実行する必要があり、メインメモリーを直接読み書きすることはできません。異なるスレッドは、互いの作業メモリ内の変数に直接アクセスできません。スレッド間で変数を送信するには、それぞれの作業メモリとメインメモリ間のデータ同期が必要です。
2)注文の並べ替え。
ここでは、最初にコードの一部を見てください
public class PossibleReordering {
static int x = 0, y = 0 ;
static int a = 0, b = 0 ;
public static void sain(String{
} args) throws Int erruptedException {
Thread one = new Thread(new Runnable() [
public void run() {
a = 1;
x = b;
}
]);
Thread other = new Thread(new Runnable() [
public void run() {
b = 1;
y = a;
}
]);
one.start() ; other.start(),
one.join() ; oher.join();
Systen.out.println("("+x+","+y+")");
操作の結果は、(1,0)、(0,1)、または(1,1)の場合もあれば、(0,0)の場合もあります。なぜなら、実際の運用では、コード命令はコードステートメントの順序で厳密に実行されない場合があるからです。最新のマイクロプロセッサのほとんどは、アウトオブオーダー実行(OoOEまたはOOE)方式を採用しており、条件が許せば、フェッチを回避して、現在すぐに実行できる後続の命令を直接実行します。次の命令に必要なデータによる待機3。アウトオブオーダー実行テクノロジーにより、プロセッサーは実行効率を大幅に向上させることができます。そして、これが順序の再配置です。
3)メモリバリアメモリバリアは、メモリバリアとも呼ばれ、特定の条件下で並べ替えとメモリの可視性の問題を制御するために使用されるCPU命令です。
- LoadLoadバリア:このようなステートメントLoad1; LoadLoad; Load2の場合、Load2によって読み取られるデータとそれに続く読み取り操作にアクセスする前に、Load1によって読み取られるデータが読み取られていることが保証されます。
- StoreStoreバリア:このようなステートメントStore1; StoreStore; Store2の場合、Store2以降の書き込み操作が実行される前に、Store1の書き込み操作が他のプロセッサから見えるようになっています。
- LoadStoreバリア:このようなステートメントLoad1; LoadStore; Store2の場合、Store2以降の書き込み操作がフラッシュされる前に、Load1によって読み取られるデータが読み取られていることが保証されます。
- StoreLoadバリア:このようなステートメントStore1; StoreLoad; Load2の場合、Load2および後続のすべての読み取り操作が実行される前に、Store1の書き込みがすべてのプロセッサに表示されることが保証されます。そのオーバーヘッドは、4つの障壁の中で最大です。ほとんどのプロセッサ実装では、このバリアはユニバーサルバリアであり、他の3つのメモリバリアの機能を果たします。
4)発生前の原則
- シングルスレッドの発生前の原則:同じスレッドで、発生前に続く操作を前の操作の前に記述します。ロックの発生前の原則:同じロックのロック解除操作が発生します-このロックのロック操作の前に。
- 揮発性の発生前の原則:揮発性変数の書き込み操作は、この変数に対する操作の前に発生します(もちろん、書き込み操作も含まれます)。
- 発生前の遷移性の原則:A操作が発生する場合-B操作の前に、B操作が発生する-C操作の前に、次にA操作が発生する-C操作の前に。
- スレッド開始の発生前の原則:同じスレッドの開始メソッドが発生します-このスレッドの他のメソッドの前に。
- スレッド中断の発生前の原則:スレッド中断メソッドの発生前の呼び出しは、中断されたスレッドの割り込み送信を検出するコードです。
- スレッド終了の発生前の原則:スレッド内のすべての操作は、スレッド終了の検出前に発生します。
- オブジェクト作成の発生前の原則:オブジェクトの初期化は、finalizeメソッド呼び出しの前に完了します。
7.あなたが知っているクラスローダーについて簡単に話してください、それは親の委任を破ることができますか、そしてどのように?
(1)考える
最初にクラスローダーとは何かを説明し、インタビュアーのために絵を描いてから、クラスローダーの意味について話し、親委任モデルについて話し、最後に親委任モデルを破る方法を説明します。
(2)私の答え
- クラスローダーとは何ですか?
クラスローダーは、指定された完全修飾名に従ってクラスファイルをJVMメモリにロードし、それをClassオブジェクトに変換します。
- Bootstrap ClassLoader:C ++言語(HotSpot用)によって実装され、<JAVA_HOME> \ libディレクトリに格納されているクラスライブラリまたは-Xbootclasspathパラメータで指定されたパスをメモリにロードします。
- その他のクラスローダー:Java言語で実装され、抽象クラスClassLoaderから継承されます。
といった:
- Extension ClassLoader:<JAVA_HOME> \ lib \ extディレクトリまたはjava.ext.dirsシステム変数で指定されたパスにあるすべてのクラスライブラリをロードする役割を果たします。
アプリケーションClassLoader(アプリケーションClassLoader)。指定されたクラスライブラリをユーザーのクラスパスにロードする責任があり、このクラスローダーを直接使用できます。一般に、カスタムクラスローダーがない場合、このローダーがデフォルトで使用されます。
2)親の委任モデル
親委任モデルの作業プロセスは、クラスローダーがクラスロード要求を受信した場合、最初にクラスをロードしようとせず、要求を親クラスローダーに委任して完了することです。これはすべてのクラスローダーに当てはまります。親ローダーが検索範囲内で指定されたクラスを見つけられない場合(つまり、ClassNotFoundException)にのみ、子ローダーはそれを単独でロードしようとします。
親委任モデルの図:
3)なぜ親委任モデルが必要なのですか?
ここで、最初に考えてみてください。親の委任がない場合、ユーザーは同じ名前のjava.lang.Object、同じ名前のjava.lang.Stringを定義し、それをClassPathに配置してから、クラス間で配置できます。比較結果とクラスの一意性は保証されませんが、なぜ親の委任モデルが必要なのですか?メモリ内の同じバイトの複数のコピーを防止します
4)親の委任モデルを破る方法は?
親委任メカニズムを解除するには、ClassLoaderクラスを継承するだけでなく、loadClassメソッドとfindClassメソッドを書き直す必要があります。
8.知っている主なJVMパラメータについて教えてください
(1)考える
スタック構成関連、ガベージコレクター関連、および補助情報関連について話すことができます。
(2)私の答え
1)スタック構成関連
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-
XX:MaxPermSize = 16m
-
XX:NewRatio = 4
-
XX:SurvivorRatio = 4
-
XX:MaxTenuringThreshold = 0
-
Xmx3550m:最大ヒープサイズは3550mです。
-
Xms3550m:初期ヒープサイズを3550mに設定します。
-
Xmn2g:若い世代のサイズを2gに設定します。
-
Xss128k:各スレッドのスタックサイズは128kです。
-
XX:MaxPermSize:永続的な生成サイズを16mに設定します
-
XX:NewRatio = 4:若い世代(エデンと2つのサバイバーエリアを含む)と古い世代(永続的な世代を除く)の比率を設定します。
-
XX:SurvivorRatio = 4:若い世代のサバイバーエリアに対するエデンエリアのサイズ比を設定します。4に設定すると、2つのサバイバーエリアと1つのエデンエリアの比率は2:4であり、1つのサバイバーエリアは若い世代全体の1/6を占めます。
-
XX:MaxTenuringThreshold = 0:ごみの最大経過時間を設定します。0に設定すると、若い世代のオブジェクトはサバイバーエリアを通過せず、古い世代に直接入ります。
2)ガベージコレクター関連
-
XX:+ UseParallelGC -XX:ParallelGCThreads = 20 -XX:+ UseConcMarkSweepGC
-
XX:CMSFullGCsBeforeCompaction = 5 -XX:+ UseCMSCompactAtFullCollection:
-
XX:+ UseParallelGC:ガベージコレクターを並列コレクターとして選択します。
-
XX:ParallelGCThreads = 20:並列コレクターのスレッド数を構成します
-
XX:+ UseConcMarkSweepGC:古い世代を同時収集に設定します。
-
XX:CMSFullGCsBeforeCompaction:コンカレントコレクターはメモリスペースを圧縮および整理しないため、一定期間実行した後に「断片化」を生成し、操作効率を低下させます。この値は、メモリスペースを圧縮および整理するためにGCを実行する回数を設定します。
-
XX:+ UseCMSCompactAtFullCollection:旧世代の圧縮をオンにします。パフォーマンスに影響を与える可能性がありますが、断片化を排除できます
3)関連する補助情報
-
XX:+ PrintGC -XX:+ PrintGCDetails
-
XX:+ PrintGC出力フォーム:
[GC 118250K-> 113543K(130112K)、0.0094143秒] [フルGC121376K-> 10414K(130112K)、0.0650971秒]
- XX:+ PrintGCDetails出力形式:
[GC [DefNew:8614K-> 781K(9088K)、0.0123035秒] 118250K-> 113543K(130112K)、0.0124633秒]
[GC [DefNew:8614K-> 8614K(9088K)、0.0000665秒] [テニュア:112761K-> 10414K(121024K)、0.0433488秒] 121376K-> 10414K(130112K)、0.0436268秒
9.スレッドスタック情報を印刷する方法は?
(1)考える
jps、top、jstackコマンドについて話し、オンライントラブルシューティングに協力してそれらに答えることができます。
(2)私の答え
jpsと入力して、プロセス番号を取得します。
top -HppidこのプロセスのすべてのスレッドのCPU時間のかかるパフォーマンスを取得します
現在のjavaプロセスのスタックステータスを表示するjstackpidコマンド
または、jstack -l> /tmp/output.txtを使用して、スタック情報をtxtファイルに出力します。
ファストスレッドスタックポジショニングを使用できます
10.ストロングリファレンス、ソフトリファレンス、ウィークリファレンス、ファントムリファレンスの違いは何ですか?
(1)考える
最初に4種類の参照の定義について説明します。これは、説明するコードと組み合わせたり、ThreadLocalMapでの弱い参照の使用について説明するように拡張したりできます。
(2)私の答え
1)強い参照
通常、新しいオブジェクトはObject obj = new Object()のように強力な参照です。メモリが不足している場合でも、JVMはこのオブジェクトを再利用するよりもOutOfMemoryエラーをスローします。
2)ソフトリファレンス
オブジェクトにソフト参照しかない場合、メモリスペースは十分であり、ガベージコレクタはそれを再利用しません。メモリスペースが不十分な場合、これらのオブジェクトのメモリは再利用されます。
SoftReference softRef = new SoftReference(str); //ソフトリファレンス
3)弱い参照
参照が弱いオブジェクトは、ライフサイクルが短くなります。ガベージコレクタースレッドがその管轄下のメモリ領域をスキャンするプロセスでは、参照が弱いオブジェクトが見つかると、現在のメモリスペースが十分であるかどうかに関係なく、そのメモリが再利用されます。
String str=new String("abc");
WeakReference<String> abcWeakRef = new WeakReference<String>(str);
str=null;
System.gc();
4)ファントムリファレンス
オブジェクトがファントム参照のみを保持している場合、それは参照がない場合と同じであり、ガベージコレクターによっていつでも収集される可能性があります。ファントム参照は、主にガベージコレクターによってリサイクルされているオブジェクトのアクティビティを追跡するために使用されます。
結論
Java言語の非常に重要な機能は、プラットフォームからの独立性です。Java仮想マシンの使用は、この機能を実現するための鍵です。一般的な高レベル言語をさまざまなプラットフォームで実行する場合は、少なくとも、さまざまなターゲットコードにコンパイルする必要があります。Java言語仮想マシンの導入後、異なるプラットフォームで実行する場合、Java言語を再コンパイルする必要はありません。Java言語使用モードJava仮想マシンは、特定のプラットフォームに関連する情報を保護するため、Java言語コンパイラはJava仮想マシンで実行されるオブジェクトコード(バイトコード)を生成するだけでよく、変更せずに複数のプラットフォームで使用できます。実行します。Java仮想マシンがバイトコードを実行すると、特定のプラットフォームで実行するためにバイトコードがマシン命令に解釈されます。
最近、仕事を見つけるのに最適な時期です。主要メーカーからのインタビューの質問と今年(2020年)の最新データを収集しました。以下はデータのスクリーンショットです(すべてのデータはドキュメントに統合され、pdfは圧縮およびパッケージ化されています) 。
困っている友達がいる場合は、ここをクリックして情報を入手できます。コード:qf