Skip to content

Instantly share code, notes, and snippets.

@todokr
Last active August 12, 2025 08:28
Show Gist options
  • Select an option

  • Save todokr/428e3b6aa99173b245d36c73e0345ded to your computer and use it in GitHub Desktop.

Select an option

Save todokr/428e3b6aa99173b245d36c73e0345ded to your computer and use it in GitHub Desktop.

楽しく学ぼうJavaのHello World

todokr

このLTでは

  • “Hello world” の出力をを通じて、Javaについての基本を理解します
  • 前提知識は不要です

WorldにHelloするまでの流れ

  • Step1. ███████████████を用意する
  • Step2. ████████████████████████を用意する
  • Step3. 実行する

WorldにHelloするまでの流れ

  • Step1. JavaでHello worldするコードを用意する
  • Step2. ████████████████████████を用意する
  • Step3. 実行する

WorldにHelloするまでの流れ

  • Step1. JavaでHello worldするコードを用意する
  • Step2. JVMの仕様どおりにクラスファイルを実行するコードを用意する
  • Step3. 実行する

Step1. JavaでHello worldするコードを用意する

Hello worldするコードを用意しておいてください インターネットからコピペでいいです

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello world");
    }
}

普通にコンパイルもしておいてください

javac Hello.java

Step2. JVMの仕様どおりにクラスファイルを実行するコードを用意する

まず JVMの仕様 をカジュアルな気持ちで読んでみよう

いろいろ書いてある

個人的なおもしろポイントをいくつか紹介します

「振り返ると、8バイトの定数に2エントリを割り当てたのは間違った選択でした」

In retrospect, making 8-byte constants take two constant pool entries was a poor choice.

はじまりは HotJava というウェブブラウザ

The HotJava browser first showcased the interesting properties of the Java programming language and platform by making it possible to embed programs inside HTML pages.

めちゃめちゃ怪しいサイト からダウンロードできるっぽい

The number of dimensions in an array is limited to 255 by the size of the dimensions opcode of the multianewarray instruction and by the constraints imposed on the multianewarray, anewarray, and newarray instructions (§4.9.1, §4.9.2).

The Specification is subject to U.S. export control laws and may be subject to export or import regulations in other countries.

キューバ, イラン, 北朝鮮, シリア, ウクライナのドンバス, ロシア, ベラルーシ, ベネズエラとかには輸出NGなので気をつけよう

JVMを実装するには

「classファイルが読めて、その中のオペレーションが実行できるだけでOK」

To implement the Java Virtual Machine correctly, you need only be able to read the class file format and correctly perform the operations specified therein.

なんかできる気がしてきた

classファイルとは

Java class ファイルとは

  • Javaのソースコードをコンパイルした結果のバイナリファイル
  • フォーマットは ここ に書いてある
  • Hello.class を自分のバイナリエディタで開いてみよう

そんなの読めるか

読める気がしないので、 javap -v しよう

classファイルを読んでみよう

The ClassFile Structure と突き合わせながら読んでみよう

その1: magic

parser.go

その2: minor_version, major_version

  • minor, major の順で並んでいる
  • たとえば Java17 だと major_version が 61
  • プレビュー版はminor_version が 65535

parser.go

その3: constant_pool_count, cp_info

  • cp_info内に(constant_pool_count - 1)個のエントリが入っている
  • エントリは定数やシンボルなど、実行に必要な情報
    • クラス名: java//lang/Object
    • メソッドのシグネチャ: println:(Ljava/lang/String;)V
    • 文字列リテラル: Hello world
    • などなど

その4: access_flags

  • access_flags: public, final, abstract とかを表す
    • ビットマスクなのでパースするとき楽しい
      • 0x0001: public
      • 0x0010: final
      • 0x0020: super
      • 0x0400: abstract
      • 0x1000: synthetic(コンパイラによって生成される、ソースコードには現れないやつ)
      • 0x2000: annotation
      • 0x4000: enum
      • 0x8000: module

this_class, super_class

  • this_class: このクラス(を示すcp_infoのインデックス)
  • super_class: 親クラス(を示すcp_infoのインデックス)

interfaces_count, interfaces

  • interfaces_count: このクラスが実装しているインターフェースの数
  • interfaces: インターフェース(を示すcp_infoのインデックス)

fields_count, fields

  • fields_count: このクラスが持つフィールドの数
  • fields: フィールドの情報

methods_count, methods

  • methods_count: このクラスが持つメソッドの数
  • methods: メソッドの情報
  • method_info が持つ attribute_info のうち、属性の種別が Code であるやつの中に、JVMが実行する命令が入っている

図解

attribute_infoのパース

  • attribute_name_indexが指すエントリをconstant_poolから取得する
  • 取得したエントリを CONSTANT_Utf8_info として解釈し、文字列を得る
  • その文字列が Code なら、Code_attribute としてパースする
  • その中にある code が実行する命令!

これでパースはOK!

各種要素をPrintする際のformatをいい感じにして、パースした結果を出力してみよう

main.go

Code_attribute.code

mainメソッドが実行する命令はこれでした

main code: [ 0xb2 0x0 0x7 0x12 0xd 0xb6 0x0 0xf 0xb1 ]

The Java Virtual Machine Instruction Set を見ながら、それぞれの命令を解釈してみよう

0xb2: getstatic

staticフィールドをconstant poolから取得する

  • そのあとの2byteは 0x0 0x07 だから indexは#7だな
  • #7はCONSTANT_Field_infoで、classIndex #8, nameAndTypeIndex #9 だな
    • #8はCONSTANT_Class_infoで、nameIndex #10だな
      • #10は CONSTANT_Utf8_infoで “java/lang/System” だな
    • #9はCONSTANT_NameAndType_infoで、nameIndex #11, descriptorIndex #12 だな
      • #11は CONSTANT_Utf8_infoで “out” だな
  • つまり System.out だな
  • スタックに System.out を積むぜ

0x12: ldc

constant poolの値を取り出してスタックに積む

  • そのあとの1byteは 0xdなのでindexは#13だな
    • #13は CONSTANT_String_infoで #14だな
      • #14は CONSTANT_Utf8_infoで “Hello world” だな
  • つまり “Hello world” だな
  • スタックに “Hello world” を積むぜ

0xb6: invokevirtual

仮想ディスパッチ(呼ぶ対象を実行時に決定)するのでvirtual

  • そのあとの2byteは 0x0 0xf だから indexは#15だな
    • #15はCONSTANT_Method_infoで classIndex #16, nameAndTypeIndex #17 だな
      • #16はCONSTANT_Class_infoで nameIndex #18だな
        • #18はCONSTANT_Utf8_infoで “java/io/PrintStream” だな
      • #17はCONSTANT_NameAndType_infoで nameIndex #19, descriptorIndex #20 だな
        • #19はCONSTANT_Utf8_infoで “println” だな
        • #20はCONSTANT_Utf8_infoで “(Ljava/lang/String;)V” だな
          • ってことは引数がStringが1つで戻り値がvoidだな
  • 引数が1つなので1つpopだな
    • “Hello world” が出てきたな
  • つまり java.io.PrintStream.println に “Hello world” をエイッと渡してバーンと実行だな

0x0b1: return

  • voidを返す
  • スタックに値が残っていたら破棄する

Step3. 実行する

  • これで完成?
  • 残念、このままでは動かせない

java.io.PrintStream.println is 何 問題

  • オレオレJVMはそんなもの知らない

作るんだよ

手抜きで作るとこんな感じ

動かしてみよう

go run main.go Hello.class

まとめ

  • Javaの基礎について学んだ
    • classファイルの構造
    • 実行のしくみ
  • JVMの仕様を読んで、classファイルをパース & 実行した
    • Hello worldだけならそんなに難しくない
      • attribute_infoのパースとinvokevirtualの実装がちょっと面倒
  • みんなも好きな言語でJVMを作ろう!

おまけ

Java6のライセンスには明確に「この用途に使ってはいけない」が書かれている

Java Platform, Standard Edition 6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment