Learn · concepts

JVMバイトコードとは?Javaクラスファイル解説

Java仮想マシンが実行する、コンパクトな命令セットがJVMバイトコードです。 .classファイルの仕組み、modsがバイトコードを書き換える理由、そしてASMがどのように関連するのかを学びましょう.

TRtrolPublished 1 min read

JVM バイトコードとは?

JVM バイトコードとは、Java Virtual Machine (JVM) が実行する命令セットのことです。Java ソースコードをコンパイルすると、コンパイラは CPU 向けの機械語を生成するのではなく、.class ファイルに格納される、コンパクトで移植性の高いバイトコードを生成します。JVM は実行時にこれらの命令を読み取り、必要に応じてネイティブコードに変換します。

この階層構造(ソースコードからバイトコード、そして機械語へ)が、Java の移植性の根源です。同じ .class ファイルは、互換性のある JVM を搭載した Windows、macOS、Linux、またはその他のシステム上で実行できます。なぜなら、JVM がローカルマシンで命令を実行する方法を知っている層だからです。「一度書けば、どこでも実行できる」。

ソースコードがバイトコードになるまで

.java ファイルは、人が書くテキストです。.class ファイルは、JVM が実行するコンパイルされた出力です。コンパイラはあなたのソースコードを読み取り、チェックし、クラスごとに 1 つの .class ファイルを書き込みます。各ファイルには、バイトコード命令、定数プール、メソッドテーブル、その他のメタデータが含まれています。

バイトコードは特定のプロセッサに依存しません。同じ .class ファイルは、Java ランタイムを搭載した Windows、macOS、Linux、またはあらゆるシステム上で実行できます。なぜなら、JVM がローカルマシンで命令を実行する方法を知っている層だからです。この移植性は、この形式の目的そのものです。

命令の見た目

バイトコードはスタックベースの命令セットです。ほとんどの操作は、オペランドスタックに値をプッシュし、ポップし、結果をプッシュします。考慮すべきレジスタ割り当てはなく、モデルは意図的にシンプルであるため、JVMはそれを迅速に検証および実行できます。

いくつかのopcodeの例を挙げます。

opcode動作
iloadintローカル変数をスタックにプッシュ
iadd2つのintをポップし、それらの合計をプッシュ
invokevirtualインスタンスメソッドを呼び出す
getfieldインスタンスフィールドを読み取る
returnメソッドから戻る

これらのopcodeを直接読むか書くことはほとんどありません。ツールが生成および消費します。ASMのようなライブラリはその役割を果たします。

モッドにとってバイトコードが重要な理由

バイトコードは、元のソースコードなしで動作を変更できるからです。Minecraftのモッドは、ゲームのソースコードを持ちません。代わりに、ディスク上のコンパイルされたゲームがあります。機能を追加または変更するには、モッドは既存のバイトコードを読み取り、書き換え、クラスがロードされる際にJVMに結果をフィードバックします。

パッチングフレームワークが、所有していないメソッドにフックを挿入する方法です。コンパイルされたメソッド内の適切な場所を見つけ、モッドのコードへの呼び出しをspliceします。バイトコードレベルで作業することで、モッドは元のJavaの1行も見たことがないにもかかわらず、ゲームにアタッチできます.

実際に、Terminusやその他のFabricベースのクライアントがMinecraftを修正する方法は、こうです。Minecraftのソースコードを持つ代わりに、コンパイルされた.classファイルを扱い、モジュール、設定画面、カスタムレンダリングといった機能を追加するために、バイトコードを書き換えます。

ASMがどのように適合するか

ASMは、バイトコードの読み書き、変換を行うJavaライブラリです。.classファイルをイベントやツリーとして解析し、コードが命令を修正できるようにし、有効な.classファイルを再び出力します。小さく、高速で、生のフォーマットに近い形で動作するため、より高レベルのモディングツールは、クラスファイルを直接解析するのではなく、ASMの上に構築します。

ほとんどのモッダーは、ASMを直接呼び出すことはありません。彼らは、オプコードを隠し、「このメソッドの開始時に私のコードを実行する」というより使いやすい方法を提供している、より高レベルのレイヤーを使用します。そのレイヤーの下では、多くの場合ASMを通じて、バイトコードが編集されています.

バイトコードからソースコードへの逆コンパイル

FernflowerやCFRといった逆コンパイラは、コンパイルされた.classファイルを読み取り可能なJavaコードに再構築します。このプロセスは完璧ではありません。名前やコメントが失われたり、特定の構造の復元が困難であったり、難読化されたコードは読めなくなったりします。しかし、通常の難読化されていないコードの場合、逆コンパイラは通常、元のコードと十分に近く、理解し作業できるほどに近い出力結果を生成します。

これはゲームの仕組みを学ぶのに役立ちますが、実際のソースコードを持つことの代わりにはなりません。逆コンパイルされたコードは依然としてコメントのないコンパイルされたロジックであり、再構築された変数名は誤解を招く可能性があります。これは読み取りツールであり、書き込みツールではありません。

よくある質問