ソースコードと機械語の中間に位置する、仮想マシン向けの中間表現。
中間コード(バイトコード)とは、人が書くソースコードと、CPUが直接実行する機械語の、ちょうど中間に位置する表現のことです。名前のとおり「中間」にあり、特定のCPU(=計算を行う頭脳。種類によって理解できる命令が違う)に依存しません。
身近な例で考えると、世界共通の「絵文字マニュアル」に似ています。日本語(ソース)を各国の言葉(機械語)にいきなり訳す代わりに、まず誰でも分かる絵文字(中間コード)にしておけば、各国にその絵文字を読める案内係(仮想マシン)がいるだけで内容が伝わります。
上の図解では、ソースコードがコンパイル(=翻訳)されて中間コードになり、その1つの中間コードが各OS(基本ソフト)上の仮想マシンで実行される流れを示しています。
中間コードは、それ単体ではCPU上で直接動きません。仮想マシン(VM=Virtual Machine、中間コードを解釈して実行するソフトウェア)が間に入り、中間コードを読み取って実際の処理を行います。
代表的な組み合わせは次のとおりです。
・Java:ソースをコンパイルしてバイトコード(.class)を作り、JVM(Java仮想マシン)が実行する
・Python:実行時にバイトコード(.pyc)へ変換し、Pythonの仮想マシンが実行する
・C#:中間言語(CIL)を作り、.NETの実行環境が実行する
ポイントは、仮想マシンがOSやCPUの違いを吸収してくれる点です。中間コードは「VMという共通の土台」の上で動くため、その下のOSが何であっても気にせず実行できます。
中間コード方式の最大の利点は可搬性(ポータビリティ)、つまり同じプログラムを環境を選ばず動かせることです。これを表す有名な標語が Write Once, Run Anywhere(一度書けば、どこでも動く)です。
もし機械語に直接コンパイルしていたら、CPUの種類ごとに別々の実行ファイルを用意しなければなりません。中間コードなら違います。
・1度だけコンパイル:中間コードは1種類作ればよい
・各環境のVMに任せる:Windows用VM・macOS用VM・Linux用VMがそれぞれ実行する
・移植の手間が減る:環境ごとにソースを書き直さなくてよい
海外旅行で各国の電源プラグに合わせて機器を買い替える代わりに、万能変換アダプタ(=仮想マシン)を1つ持っていくイメージです。中身(中間コード)はそのままで、アダプタが現地の事情に合わせてくれるので、どこでも使えます。
CPU(=計算を行う部品)は、メーカーや設計によって理解できる命令の種類が異なります。たとえばパソコンによく使われる x86 という設計と、スマートフォンに多い ARM という設計では、同じ「足し算をしろ」という命令でも、数字の並べ方や命令コードが違います。
これが中間コードを生み出した根本的な理由です。
・機械語直接変換の問題:1つのプログラムを各CPUに合わせて別々に変換・テスト・配布しなければならない
・中間コードの解決策:変換は1回だけ。各CPU向けのVMが「中間コードを自分の機械語に読み替える」役割を担う
・VMがコストを肩代わり:プログラムの作者ではなく、VMの開発者がCPUごとの対応をすることで全員が楽になる
たとえるなら、世界中のホテルが「全室に変換アダプタを備え付ける」ようにしたおかげで、旅行者(プログラム作者)が自分でアダプタを持参しなくてよくなった、というイメージです。各ホテル(VM)が自分の電源規格に合わせて対応しているのでどこでも同じ機器が使えます。
中間コード方式には可搬性という大きな利点がある一方、VMが間に入る分だけ実行速度がやや遅くなるという側面もあります。VMが中間コードを読んでから実際のCPU命令に変換するステップが余分にかかるためです。
なぜ今も広く使われているのか。この速度の差を大幅に縮めたのがJITコンパイラ(=実行しながら機械語に変換する仕組み)です。よく使う部分だけを機械語に変えてしまえば、その後は速く動くため、実用上の差はほとんどなくなります。
・可搬性が必要な場面:ブラウザ上のJavaScript・Javaアプリなど、環境が多様な場面では中間コード方式が選ばれる
・速度を最優先する場面:組み込み機器(家電などに入るコンピュータ)やゲームなどでは、機械語に直接コンパイルする方式が選ばれることも多い
どちらが「正解」ではなく、目的に応じて使い分けるものです。「移植しやすさ」と「実行の速さ」のバランスを意識して選択されています。