こんにちは!ワーパパエトウです!
今回はJavaで書いたソースコードがどのようにして実行されていくのかについて解説します。
人間が書いたプログラムをパソコンが理解するまで
Javaで書いたプログラム(以降、Javaファイル)は当然人間が書いたものですので、そのままだとパソコンは理解することができません。
そこでパソコンが理解できるようにJavaファイルを変換する必要があります。
変換していく順番は下記のとおりです。

JavaのソースコードはJavaのコンパイラを用いて中間コード(クラスファイル)を生成します。
この中間コードを元にJVM(JITコンパイラ)がネイティブコードに変換しながらプログラムを実行していくという訳です。
ここで登場してきたJVMについても少し触れておきましょう。
JVMとは何者か?
Java仮想マシン(以降、JVM)はJavaで作ったプログラムを動かすための計算機のようなものだと思ってください。
JVMは中間コード(クラスファイル)を各プラットフォームに応じたネイティブコードに変換して実行してくれます。
Java言語が『Write once, run anywhere(一度プログラムを書けば、どこでも実行できる)』 と言われているのは、このJVMのおかげです。
JVMがプログラムを処理する流れ

JVMはスタックを中心に処理を進めていきます。
処理をする際は複数のスレッドがあり、各スレッドにはプログラムカウンタとスタックを1つづつ用意しています。
また、各スタックは複数のフレームでできており、各フレームはローカル変数とオペランドスタックで構成されています。

Javaのスレッドを起動すると、まず初めにJVMはメモリ上にスタックを割り当てます。
スタックとはデータを後入れ先出し(LIFO: Last In First Out)する構造のメモリ領域のことを指します。
そして、ディスク上にあるクラスファイルからメソッドの内容やローカル変数をコピーして、このスタックにプッシュします。
この時プッシュされたデータの一つ一つの塊のことをフレームと呼びます。

フレームBのメソッドBにメソッドCを呼び出す記述があったとします。
するとJVMはメソッドCが記述されたフレームCを新たにプッシュしてその処理を実行します。
このようにJVMはスタックの一番最後に追加したフレーム(以降、カレントフレーム)を処理するようにできています。
そして、カレントフレームの処理がすべて終わるとそのフレームを削除して、呼び出し元のフレームの処理を再開するのです。

例えば、フレームBのメソッドの内容に別メソッドを呼ぶ処理が記述されていたとします。
その場合、フレームCがスタックにプッシュされますが、このとき呼び出し元であるフレームBの
ローカル変数を参照したいとしても、フレームを越えてデータを参照することは出来ません。
そのため、フレームCを作成する際にフレームBのローカル変数のデータをフレームCにコピーしておきます。
このフレームCからフレームBへのデータのコピーのことを「引数」と呼びます。
また、フレームCの処理結果をフレームBにて使用したい場合については、フレームCを削除する前にフレームBに結果をコピーしておきます。
このフレームCからフレームBへの結果のコピーのことを「戻り値」と呼びます。

インスタンスなどの大きなオブジェクトについては生成するとヒープに置かれます。
フレーム間でこのインスタンスを受け渡す場合はヒープ上の番地(以降、参照)を渡してあげます。
このことを「参照渡し」と呼びます。
さいごにまとめ
今回はJavaプログラムがパソコンの裏側でどのように動いているのかについて解説してきました。
この動作の仕組みを理解しておくと思いもよらないところで役に立つことがあると思いますので、頭の片隅に置いておいてください。
今回はスタック中心の解説でしたが、ヒープ中心の記事も書いてますので参考にしてください。

では今回はここまで、次回お会いしましょう!

コメント