コンテンツ:
- 導入
- バイトコードへのコンパイル
- プログラムのコンパイルと実行の例
- 仮想マシン上でプログラムを実行する
- ジャストインタイム (JIT) コンパイル
- 結論
1. はじめに
こんにちは、みんな!今日は、Java で書かれたアプリケーションを実行した後、JVM (Java 仮想マシン) の内部で何が起こるかについての知識を共有したいと思います。最近では、JVM の内部について考えたり、Java コードをコンパイルして実行したりすることを避けるのに役立つ流行の開発環境が存在します。そのため、新しい開発者はこれらの重要な側面を見逃してしまう可能性があります。同時に、このテーマに関する質問は面接でもよく聞かれるので、記事を書くことにしました。
2. バイトコードへのコンパイル
理論から始めましょう。アプリケーションを作成するときは、拡張子付きのファイルを作成し
.java
、そのファイルに Java プログラミング言語でコードを配置します。人間が判読できるコードを含むこのようなファイルは、
ソース コード ファイルと呼ばれます。ソース コード ファイルの準備ができたら、それを実行する必要があります。しかし、その段階では人間にしか理解できない情報が含まれています。Java はマルチプラットフォームのプログラミング言語です。これは、Java で書かれたプログラムは、専用の Java ランタイム システムがインストールされている任意のプラットフォームで実行できることを意味します。このシステムは Java 仮想マシン (JVM) と呼ばれます。プログラムをソース コードから JVM が理解できるコードに変換するには、プログラムをコンパイルする必要があります。JVM が理解するコードはバイトコードと呼ばれ、仮想マシンがその後実行する一連の命令が含まれています。
javac
ソース コードをバイトコードにコンパイルするには、 JDK (Java Development Kit) に含まれるコンパイラーがあります。コンパイラは、
.java
プログラムのソース コードを含む拡張子 のファイルを入力として受け入れ、出力として、
.class
仮想マシンでプログラムを実行するために必要なバイトコードを含む拡張子 のファイルを生成します。プログラムがバイトコードにコンパイルされると、仮想マシンを使用して実行できるようになります。
3. プログラムのコンパイルと実行例
ファイル に含まれる単純なプログラムがあるとします
Calculator.java
。このプログラムは 2 つの数値コマンドライン引数を受け取り、それらの加算結果を出力します。
class Calculator {
public static void main(String[] args){
int a = Integer.valueOf(args[0]);
int b = Integer.valueOf(args[1]);
System.out.println(a + b);
}
}
このプログラムをバイトコードにコンパイルするには、
javac
コマンド ラインでコンパイラを使用します。
javac Calculator.java
コンパイル後、バイトコードを含むファイルを出力として受け取ります
Calculator.class
。これは、コンピューターにインストールされている Java マシンを使用して、コマンド ラインで java コマンドを使用して実行できます。
java Calculator 1 2
ファイル名の後に、2 つのコマンド ライン引数 (数値 1 と 2) が指定されていることに注意してください。プログラムの実行後、数値 3 がコマンド ラインに表示されます。上の例では、独自に存在する単純なクラスがありました。 。しかし、クラスが何らかのパッケージに含まれている場合はどうなるでしょうか? 次の状況をシミュレートしてみましょう。ディレクトリを作成し
src/ru/javarush
、そこにクラスを配置します。これで次のようになります (ファイルの先頭にパッケージ名を追加しました)。
package ru.javarush;
class Calculator {
public static void main(String[] args){
int a = Integer.valueOf(args[0]);
int b = Integer.valueOf(args[1]);
System.out.println(a + b);
}
}
次のコマンドを使用してそのようなクラスをコンパイルしましょう。
javac -d bin src/ru/javarush/Calculator.java
この例では、
-d bin
コンパイルされたファイルを
bin
directory に似た構造のディレクトリに配置する追加のコンパイラ オプションを使用しましたが
src
、ディレクトリは
bin
事前に作成しておく必要があります。この手法は、ソース コード ファイルとバイトコード ファイルの混同を避けるために使用されます。コンパイルされたプログラムを実行する前に、概念を説明する価値があります
classpath
。
Classpath
仮想マシンがパッケージおよびコンパイルされたクラスを検索する際の相対パスです。つまり、この方法で、ファイル システム内のどのディレクトリが Java パッケージ階層のルートであるかを仮想マシンに伝えます。
Classpath
プログラム起動時にフラグを使用して指定できます
-classpath
。次のコマンドを使用してプログラムを起動します。
java -classpath ./bin ru.javarush.Calculator 1 2
この例では、クラスが存在するパッケージの名前を含む、クラスの完全な名前が必要でした。最終的なファイル ツリーは次のようになります。
├── src
│ └── ru
│ └── javarush
│ └── Calculator.java
└── bin
└── ru
└── javarush
└── Calculator.class
4. 仮想マシンによるプログラムの実行
そこで、書かれたプログラムを起動しました。しかし、コンパイルされたプログラムが仮想マシンによって起動されるとどうなるでしょうか? まず、コンパイルとコード解釈の概念が何を意味するかを理解しましょう。
コンパイルとは、高レベルのソース言語で書かれたプログラムを、マシンコードに似た低レベル言語の同等のプログラムに変換することです。
解釈とは、ソース プログラムまたはリクエストの演算子ごと (コマンドごと、行ごと) の分析、処理、および即時実行です (プログラムを実行せずに変換するコンパイルとは対照的です)。Java 言語にはコンパイラ (
javac
) とインタプリタ (バイトコードを 1 行ずつマシンコードに変換し、即座に実行する仮想マシン) の両方があります。したがって、コンパイルされたプログラムを実行すると、仮想マシンはそのプログラムの解釈、つまりバイトコードからマシンコードへの行ごとの変換と実行を開始します。残念ながら、純粋なバイトコードの解釈は非常に長いプロセスであり、競合他社に比べて Java の速度が遅くなります。これを回避するために、仮想マシンによるバイトコードの解釈を高速化するメカニズムが導入されました。このメカニズムは、ジャストインタイム コンパイル (JITC) と呼ばれます。
5. ジャストインタイム (JIT) コンパイル
簡単に言えば、ジャストインタイム コンパイルのメカニズムは次のとおりです。プログラム内に何度も実行されるコード部分がある場合、それらを一度マシン コードにコンパイルして、今後の実行を高速化できます。プログラムのそのような部分をマシン コードにコンパイルした後、その後プログラムのこの部分を呼び出すたびに、仮想マシンはコンパイルされたマシン コードを解釈するのではなく直ちに実行するため、プログラムの実行が自然に高速化されます。プログラムの高速化は、メモリ消費量を増やし (コンパイルされたマシン コードをどこかに保存する必要があります)、プログラム実行中のコンパイルに費やす時間を増やすことによって達成されます。JIT コンパイルはかなり複雑なメカニズムなので、詳しく見てみましょう。バイトコードからマシンコードへの JIT コンパイルには 4 つのレベルがあります。コンパイル レベルが高くなるほど複雑になりますが、同時にそのようなセクションの実行は、それより低いレベルのセクションよりも高速になります。JIT - コンパイラは、各プログラム フラグメントが実行される頻度に基づいて、各プログラム フラグメントに設定するコンパイル レベルを決定します。内部では、JVM は 2 つの JIT コンパイラー (C1 と C2) を使用します。C1 コンパイラはクライアント コンパイラとも呼ばれ、第 3 レベルまでのコードのみをコンパイルできます。C2 コンパイラは、4 番目の最も複雑で最速のコンパイル レベルを担当します。
上記のことから、単純なクライアント アプリケーションの場合は、C1 コンパイラを使用する方が有益であると結論付けることができます。この場合、アプリケーションの起動速度が重要であるためです。サーバーサイドの寿命の長いアプリケーションは起動に時間がかかることがありますが、将来的には迅速に動作して機能を実行する必要があります。ここでは C2 コンパイラが適しています。
-client
x32 バージョンの JVM で Java プログラムを実行する場合、およびフラグ を使用して、使用するモードを手動で指定できます
-server
。このフラグを指定すると、
-client
JVM は複雑なバイトコードの最適化を実行しなくなり、アプリケーションの起動時間が短縮され、メモリ消費量が削減されます。フラグを指定すると、
-server
複雑なバイトコードの最適化によりアプリケーションの起動に時間がかかり、マシンコードを保存するためにより多くのメモリを使用しますが、将来的にはプログラムの実行が高速化されます。JVM の x64 バージョンでは、フラグ
-client
は無視され、アプリケーション サーバー構成がデフォルトで使用されます。
6. 結論
これで、Java アプリケーションのコンパイルと実行の仕組みについての簡単な概要は終わりました。要点:
-
javacコンパイラは、プログラムのソース コードを、Java 仮想マシンがインストールされている任意のプラットフォームで実行できるバイトコードに変換します。
-
コンパイル後、JVM は結果のバイトコードを解釈します。
-
Java アプリケーションを高速化するために、JVM は、プログラムの最も頻繁に実行されるセクションをマシンコードに変換し、メモリに保存するジャストインタイム コンパイル メカニズムを使用します。
この記事が、私たちのお気に入りのプログラミング言語がどのように機能するかをより深く理解するのに役立つことを願っています。読んでいただきありがとうございます、批判は大歓迎です!
GO TO FULL VERSION