きどたかのブログ

いつか誰かがこのブログからトラブルを解決しますように。

HelloWorldで学ぶJavaのクラスファイル

暇つぶしにクラスファイルについて語ってみよう。

今回の勉強の元となるソースはこちら。

三分でクッキングできるほど甘くないですよ。

このエントリーを書くのに実際は丸一日もかけて・・・

その情熱を人材育成に使えって話だ。

 

ソース(便宜上、行番号を添えてます)

1:public class HelloWorld {
2:    public static void main(String args) {
3:        System.out.println("Hello World!");
4:    }
5:}

 

上記のソースはコンパイルを終えるとこんな感じのバイナリになりました。

OracleでもIBMでも同じでした。

両者のjavapによる結果も同じものになりました。

Eclipseだとバイナリからして全然違うんですけどね。

 

バイナリファイル

CAFEBABE 00000032 001D0A00 06000F09 
00100011 0800120A 00130014 07001507 
00160100 063C696E 69743E01 00032829 
56010004 436F6465 01000F4C 696E654E 
756D6265 72546162 6C650100 046D6169 
6E010016 285B4C6A 6176612F 6C616E67 
2F537472 696E673B 29560100 0A536F75 
72636546 696C6501 000F4865 6C6C6F57 
6F726C64 2E6A6176 610C0007 00080700 
170C0018 00190100 0C48656C 6C6F2057 
6F726C64 2107001A 0C001B00 1C01000A 
48656C6C 6F576F72 6C640100 106A6176 
612F6C61 6E672F4F 626A6563 74010010 
6A617661 2F6C616E 672F5379 7374656D 
0100036F 75740100 154C6A61 76612F69 
6F2F5072 696E7453 74726561 6D3B0100 
136A6176 612F696F 2F507269 6E745374 
7265616D 01000770 72696E74 6C6E0100 
15284C6A 6176612F 6C616E67 2F537472 
696E673B 29560021 00050006 00000000 
00020001 00070008 00010009 0000001D 
00010001 00000005 2AB70001 B1000000 
01000A00 00000600 01000000 01000900 
0B000C00 01000900 00002500 02000100 
000009B2 00021203 B60004B1 00000001 
000A0000 000A0002 00000003 00080004 
0001000D 00000002 000E

  

クラスファイルフォーマットの話をするので確かな資料を示しましょう。

これはJVM SpecのSecond Editionからの引用になります。

4.1 The ClassFile Structure

 

クラスファイルフォーマット(一部)

    ClassFile {
        u4 magic;
    	u2 minor_version;
    	u2 major_version;
    	u2 constant_pool_count;
    	cp_info constant_pool[constant_pool_count-1];
    	u2 access_flags;
    	u2 this_class;
    	u2 super_class;
    	u2 interfaces_count;
    	u2 interfaces[interfaces_count];
    	u2 fields_count;
    	field_info fields[fields_count];
    	u2 methods_count;
    	method_info methods[methods_count];
    	u2 attributes_count;
    	attribute_info attributes[attributes_count];
    }

 

全ての情報を引用するのは厳しいので上記はその一部だと考えてください。

このさき、コンスタントプールのところは特に理解しにくいでしょう。

先頭1バイトで種類が分かり、それに応じてレイアウトが変わります。

 

マッピング

CAFEBABE                            | u4 magic
                                    |       CAFEBABE 
         0000                       | u2 ClassFile minor_version = 0 
             0032                   | u2 ClassFile major_version = 50 
                  001D              | u2 ClassFile constant_pool_count = 29 

コンスタントプールはここから。

                      0A            | u1 CONSTANT_Methodref_info tag #1
                                    |  0A = CONSTANT_Methodref 
                        00 06       | u2 CONSTANT_Methodref_info class_index
                                    |  0006 = #6 
                             000F   | u2 CONSTANT_Methodref_info name_and_type_index
                                    |  000F = #15 
                                 09 | u1 CONSTANT_Fieldref_info tag #2
                                    |  09 = CONSTANT_Fieldref 
0010                                | u2 CONSTANT_Fieldref_info class_index
                                    |  0010 = #16 
    0011                            | u2 CONSTANT_Fieldref_info name_and_type_index
                                    |  0011 = #17
         08                         | u1 CONSTANT_String_info tag #3
                                    |  08 = CONSTANT_String 
           0012                     | u2 CONSTANT_String_info string_index
                                    |  0012 = #18
               0A                   | u1 CONSTANT_Methodref_info tag #4
                                    |  0A = CONSTANT_Methodref
                  0013              | u2 CONSTANT_Methodref_info class_index
                                    |  0013 = #19
                      0014          | u2 CONSTANT_Methodref_info name_and_type_index
                                    |  0014 = #20 
                           07       | u1 CONSTANT_Class_info tag #5
                                    |  07 = CONSTANT_Class
                             0015   | u2 CONSTANT_Class_info name_index
                                    |  15 = #21
                                 07 | u1 CONSTANT_Class_info tag #6
                                    |  07 = CONSTANT_Class
0016                                | u2 CONSTANT_Class_info name_index
                                    |  16 = #22
    01                              | u1 CONSTANT_Utf8_info tag #7
                                    |  01 = CONSTANT_Utf8
      00 06                         | u2 CONSTANT_Utf8_info length
                                    |  0006 = 6 
           3C696E 69743E            | u6 CONSTANT_Utf8_info bytes
                                    |  3C696E69743E
                                    |   < i n i t >
                        01          | u1 CONSTANT_Utf8_info tag #8
                                    |  01 = CONSTANT_Utf8
                          00 03     | u2 CONSTANT_Utf8_info length
                                    |  0003 = 3 
                               2829 | u3 CONSTANT_Utf8_info bytes
56                                  |  282956
                                    |   ( ) V
  01                                | u1 CONSTANT_Utf8_info tag #9
                                    |  01 = CONSTANT_Utf8
    0004                            | u2 CONSTANT_Utf8_info length
                                    |   0004 = 4
         436F6465                   | u4 CONSTANT_Utf8_info bytes
                                    |  436F6465
                                    |   C o d e
              01                | u1 CONSTANT_Utf8_info tag #10
                                |  01 = CONSTANT_Utf8
                000F            | u2 CONSTANT_Utf8_info length
                                |  000F = 15 
                    4C 696E654E | u15 CONSTANT_Utf8_info bytes 
756D6265 72546162 6C65              |  4C696E654E756D6265725461626C65
                                    |   L i n e N u m b e r T a b l e     
                      01            | u1 CONSTANT_Utf8_info tag #11
                                    |  01 = CONSTANT_Utf8
                        00 04       | u2 CONSTANT_Utf8_info length
                                    |  0004 = 4 
                             6D6169 | u4 CONSTANT_Utf8_info bytes
6E                                  |  6D61696E
                                    |   m a i n
  01                                | u1 CONSTANT_Utf8_info tag #12
                                    |  01 = CONSTANT_Utf8
    0016                            | u2 CONSTANT_Utf8_info length
                                    |  0016 = 22 
         285B4C6A 6176612F 6C616E67 | u22 CONSTANT_Utf8_info bytes
2F537472 696E673B 2956              |  285B4C6A6176612F6C616E672F537472696E673B2956
                                    |   ( [ L j a v a / l a n g / S t r i n g ; ) V
                      01            | u1 CONSTANT_Utf8_info tag #13
                                    |  01 = CONSTANT_Utf8
                        00 0A       | u2 CONSTANT_Utf8_info length
                                    |   000A = 10 
                             536F75 | u10 CONSTANT_Utf8_info bytes
72636546 696C65                     |   536F7572636546696C65
                                    |    S o u r c e F i l e
               01                   | u1 CONSTANT_Utf8_info tag #14
                                    |  01 = CONSTANT_Utf8
                  000F              | u2 CONSTANT_Utf8_info length
                                    |  000F = 15
                      4865 6C6C6F57 | u15 CONSTANT_Utf8_info bytes
6F726C64 2E6A6176 61                |  48656C6C6F576F726C642E6A617661
                                    |   H e l l o W o r l d . j a v a
                    0C              | u1 CONSTANT_NameAndType_info tag #15
                                    |  0C = CONSTANT_NameAndType
                      0007          | u2 CONSTANT_NameAndType_info name_index
                                    |  0007 = #7
                           0008     | u2 CONSTANT_NameAndType_info descriptor_index
                                    |  0008 = #8
                               07   | u1 CONSTANT_Class_info tag #16
                                    |  07 = CONSTANT_Class
                                 00 | u2 CONSTANT_Class_info name_index
17                                  |  0017 = #23
  0C                                | u1 CONSTANT_NameAndType_info tag #17
                                    |  0C = CONSTANT_NameAndType
    0018                            | u2 CONSTANT_NameAndType_info name_index
                                    |  0018 = #24
         0019                       | u2 CONSTANT_NameAndType_info descriptor_index
                                    |  0019 = #25
             01                     | u1 CONSTANT_Utf8_info tag #18
                                    |  01 = CONSTANT_Utf8
               00 0C                | u2 CONSTANT_Utf8_info length
                                    |  000C = 12
                    48656C 6C6F2057 | u12 CONSTANT_Utf8_info bytes
6F726C64 21                         |  48656C6C6F20576F726C6421
                                    |   H e l l o   W o r l d !
           07                       | u1 CONSTANT_Class_info tag #19
                                    |  07 = CONSTANT_Class
             001A                   | u2 CONSTANT_Class_info name_index
                                    |  001A = #26
                  0C                | u1 CONSTANT_NameAndType_info tag #20
                                    |  0C = CONSTANT_NameAndType
                    001B            | u2 CONSTANT_NameAndType_info name_index
                                    |  001B = #27
                        00 1C       | u2 CONSTANT_NameAndType_info descriptor_index
                                    |  001C = #28
                             01     | u1 CONSTANT_Utf8_info tag #21
                                    |  01 = CONSTANT_Utf8
                               000A | u2 CONSTANT_Utf8_info length
                                    |  000A = 10
48656C6C 6F576F72 6C64              | u10 CONSTANT_Utf8_info bytes
                                    |  48656C6C6F576F726C64
                                    |   H e l l o W o r l d
                      01            | u1 CONSTANT_Utf8_info tag #22
                                    |  01 = CONSTANT_Utf8
                        00 10       | u2 CONSTANT_Utf8_info length
                                    |  0010 = 16
                             6A6176 | u16 CONSTANT_Utf8_info bytes
612F6C61 6E672F4F 626A6563 74       |  6A6176612F6C616E672F4F626A656374
                                    |   j a v a / l a n g / O b j e c t
                             01     | u1 CONSTANT_Utf8_info tag #23
                                    |  01 = CONSTANT_Utf8
                               0010 | u2 CONSTANT_Utf8_info length
                                    |  0010 = 16
6A617661 2F6C616E 672F5379 7374656D | u16 CONSTANT_Utf8_info bytes
                                    |  6A6176612F6C616E672F53797374656D
                                    |   j a v a / l a n g / S y s t e m
01                                  | u1 CONSTANT_Utf8_info tag #24
                                    |  01 = CONSTANT_Utf8
  0003                              | u2 CONSTANT_Utf8_info length
                                    |  0003 = 3
      6F 7574                       | u3 CONSTANT_Utf8_info bytes
                                    |  6F7574
                                    |   o u t
             01                     | u1 CONSTANT_Utf8_info tag #25
                                    |  01 = CONSTANT_Utf8
               00 15                | u2 CONSTANT_Utf8_info length
                                    |  0015 = 21
                    4C6A61 76612F69 | u21 CONSTANT_Utf8_info bytes
6F2F5072 696E7453 74726561 6D3B     |  4C6A6176612F696F2F5072696E7453747265616D3B
                                    |   L j  a v a / i o / P r i n t S t r e a m
                               01   | u1 CONSTANT_Utf8_info tag #26
                                    |  01 = CONSTANT_Utf8
                                 00 | u2 CONSTANT_Utf8_info length
13                                  |  0013 = 19
  6A6176 612F696F 2F507269 6E745374 | u19 CONSTANT_Utf8_info bytes
7265616D                            |  6A6176612F696F2F5072696E7453747265616D
                                    |   j a v a / i o / P r i n t S t r e a m
         01                         | u1 CONSTANT_Utf8_info tag #27
                                    |  01 = CONSTANT_Utf8
           0007                     | u2 CONSTANT_Utf8_info length
                                    |  0007 = 7
               70 72696E74 6C6E     | u7 CONSTANT_Utf8_info bytes
                                    |  7072696E746C6E
                                    |   p r i n t l n
                               01   | u1 CONSTANT_Utf8_info tag #28
                                    |  01 = CONSTANT_Utf8
                                 00 | u2 CONSTANT_Utf8_info length
15                                  |  0015 = 21
  284C6A 6176612F 6C616E67 2F537472 | u19 CONSTANT_Utf8_info bytes
696E673B 2956                       |   284C6A6176612F6C616E672F537472696E673B2956
                                         ( L j a v a / l a n g / S t r i n g ; ) V

ここまでがコンスタントプール。全部で28個プールされてます。

コンスタントプールにおさめられる情報の種類はこちら。

Constant Type

あとで出てきますがjavapにでてくるAscizというのはCONSTANT_Utf8のことです。

コンスタントプールを直訳するなら定数プールになってしまいますが、よく使われている定数とは性質が異なるので、変な誤解を与えるのが嫌でコンスタントプールという言葉を使うことを避けてます。JVM方面にも腕のたつJavaエンジニアにしか使わないほうがいい、あとはバイトコード操作するライブラリを使った経験のある人とか。

             0021                   | u2 ClassFile access_flags
                                    |  0021 = 0020 ACC_SUPER
                                    |         0001 ACC_PUBLIC
                  0005              | u2 ClassFile this_class
                                    |  #5 => #21 HelloWorld
                      0006          | u2 ClassFile super_class #6
                           0000     | u2 ClassFile interfaces_count 0
                               0000 | u2 ClassFile fields_count 0
0002                                | u2 ClassFile method_count 2

インターフェースやフィールドがないので若干さみしいクラスファイルになってます。メソッドカウントが2なので、ここからはメソッドの情報です。

メソッド情報の1つ目(暗黙で生成されたコンストラクタ

    0001                            | u2 method_info access_flags
                                    |  0001 = 0001 ACC_PUBLIC
         0007                       | u2 method_info name_index
                                    |  #7 <init>
             0008                   | u2 method_info descriptor_index
                                    |  #8 ()V
                  0001              | u2 method_info attributes_count
                      0009          | u2 Code_attribute attribute_name_index
                                    |  #9 Code
                           0000001D | u4 Code_attribute attribute_length = 29
0001                                | u2 Code_attribute max_stack = 1
    0001                            | u2 Code_attribute max_locals = 1
         00000005                   | u4 Code_attribute code_length = 5
                  2AB70001 B1       | u5 Code_attribute code
                             0000   | u2 Code_attribute exception_table_length = 0
                                 00 | u2 Code_attribute attributes_count = 1
01                                  |
  000A                              | u2 LineNumberTable_attribute attribute_name_index
                                    |  #10 = LineNumberTable
      00 000006                     | u4 LineNumberTable_attribute attribute_length = 6
               00 01                | u2 LineNumberTable_attribute line_number_table_length
                    0000            | u2 LineNumberTable_attribute start_pc
                        00 01       | u2 LineNumberTable_attribute line_number

method_infoのattribute_lengthが1で、Code_attributeがついてます。そしてCode_attributeのattribute_countも1がついており、LineNumberTable_attributeが添えられてます。コンパイラーの設定でこのLineNumberTable_attributeをつけなくすることができます。デバッグが大変なのでまずやらないですね。

お気づきでしょうが、ソース上に書いてないのに行数がでます。このように暗黙で作られたものは1行目とみなされます。他の例でいくと、総称メソッドなんかも暗黙でメソッドが作られるのでこういうのになります。

method_infoのattribute一覧

  • Code_attribute
  • Exceptions_attribute これはthrows句の部分です
  • Synthetic_attribute 合成メソッド。例えば総称メソッドで合成メソッドができます。
  • Deprecated_attribute これはmethod_info以外でも使われます。ClassFileとfield_infoにも。

Synthetic_attributeもClassFile、field_infoで使われます。Code_attributeとExceptions_attributeはmethod_info固有のattributeのはずです。

 

メソッド情報の2つ目(mainメソッド)

                             0009   | u2 method_info access_flags
                                    |  0009 = 00008 ACC_STATIC
                                    |         00001 ACC_PUBLIC
                                 00 | u2 method_info name_index
0B                                  |  #11 = main                                 
  000C                              | u2 method_info descriptor_index
                                    |  #12 = ([Ljava/lang/String;)V
      00 01                         | u2 method_info attributes_count
           0009                     | u2 Code_attribute attribute_name_index
                                    |  #9 Code
               00 000025            | u4 Code_attribute attribute_length
                        00 02       | u2 Code_attribute max_stack
                             0001   | u2 Code_attribute max_locals 
                                 00 | u4 Code_attribute code_length
000009                              |  00000009 = 9
      B2 00021203 B60004B1          | u9 Code_attribute code
                           0000     | u2 Code_attribute exception_table_length
                               0001 | u2 Code_attribute attributes_count
000A                                | u2 LineNumberTable_attribute attribute_name_index
                                    |  #10 = LineNumberTable
    0000 000A                       | u4 LineNumberTable_attribute attribute_length
             0002                   | u2 LineNumberTable_attribute line_number_table_length
                  0000              | u2 LineNumberTable_attribute start_pc
                      0003          | u2 LineNumberTable_attribute line_number
                           0008     | u2 LineNumberTable_attribute start_pc
                               0004 | u2 LineNumberTable_attribute line_number

クラスアトリビュートの部分。オプション的なもの。

0001                                | u2 ClassFile attribute_count
    000D                            | u2 SourceFile_attribute attribute_name_index
                                    |  #13 = SourceFile
         00000002                   | u4 SourceFile_attribute attribute_length
                  000E              | u2 SourceFile_attribute sourcefile_index
                                    |  #14 = HelloWorld.java

 

今回のクラスファイルだと、Local Variable TableとException Tableが出てこない。

もっと厳密な話を言うと以下の構造は使われてない。

  • LocalVariableTable_attribute
  • Code_attributeの中のexception_table
  • Exceptions_attribute ※exception_tableとは別のものです
  • field_info
  • Synthetic_attribute
  • InnerClasses_attribute
  • Deprecated_attribute
  • ConstantValue_attribute
  • CONSTANT_Long_info
  • CONSTANT_Double_info
  • CONSTANT_Float_info
  • CONSTANT_Integer_info
  • CONSTANT_Fieldref_info
  • CONSTANT_InterfaceMethodref_info

ちなみにEclipseでのコンパイル結果では、Local Variable Tableが作られていた。

少々残念だが、これらはまたの機会にしよう。

 

ここから少しコード(OPCODE)の話をしよう。

今回はメソッドが2つなんだが、当然ながらmainメソッドの方を解説する。

B2 00021203 B60004B1 の部分。

そろそろjavapの内容を出さないと辛い。直接クラスファイルを読むなんて普通しないのでjavapの内容を見ながら話しましょう。現場で正規のjavapを使うのは手間なので、通常はEclipseのClass File Editorで見ます。今回のは正規のjavapです。自分がよく使うのは以下のオプション。

javap -classpath クラスパス -verbose -private クラス

Compiled from "HelloWorld.java"
public class HelloWorld extends java.lang.Object
  SourceFile: "HelloWorld.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method       #6.#15; //  java/lang/Object."<init>":()V
const #2 = Field        #16.#17;        //  java/lang/System.out:Ljava/io/PrintStream;
const #3 = String       #18;    //  Hello World!
const #4 = Method       #19.#20;        //  java/io/PrintStream.println:(Ljava/lang/String;)V
const #5 = class        #21;    //  HelloWorld
const #6 = class        #22;    //  java/lang/Object
const #7 = Asciz        <init>;
const #8 = Asciz        ()V;
const #9 = Asciz        Code;
const #10 = Asciz       LineNumberTable;
const #11 = Asciz       main;
const #12 = Asciz       ([Ljava/lang/String;)V;
const #13 = Asciz       SourceFile;
const #14 = Asciz       HelloWorld.java;
const #15 = NameAndType #7:#8;//  "<init>":()V
const #16 = class       #23;    //  java/lang/System
const #17 = NameAndType #24:#25;//  out:Ljava/io/PrintStream;
const #18 = Asciz       Hello World!;
const #19 = class       #26;    //  java/io/PrintStream
const #20 = NameAndType #27:#28;//  println:(Ljava/lang/String;)V
const #21 = Asciz       HelloWorld;
const #22 = Asciz       java/lang/Object;
const #23 = Asciz       java/lang/System;
const #24 = Asciz       out;
const #25 = Asciz       Ljava/io/PrintStream;;
const #26 = Asciz       java/io/PrintStream;
const #27 = Asciz       println;
const #28 = Asciz       (Ljava/lang/String;)V;

{
public HelloWorld();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   return
  LineNumberTable:
   line 1: 0


public static void main(java.lang.String);
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #3; //String Hello World!
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return
  LineNumberTable:
   line 3: 0
   line 4: 8


}

B2 00021203 B60004B1 の部分は、getstatic、ldc、invokevirtual、returnの部分。

これらのOPCODEの説明はこちら

getstatic

ldc

invokevirtual

return

 

B20002の説明

B2=getstaticのOPCODE

00=index byte1

02=index byte2

0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;

index byte2が#2で、コンスタントプールの#2で表しているFieldを取得している。

getstaticの結果(PrintStream)はオペランドスタックに積まれる。

 

1203の説明

12=ldcのOPCODE

03=index

   3:   ldc     #3; //String Hello World!

コンスタントプールの#3をオペランドスタックに積んでいる。

 

B60004の説明

B6=invokevirtualのOPCODE

00=index byte1

04=index byte2

   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V

コンスタントプールの#4にあるメソッドを呼び出している。

オペランドスタックには先ほどの文字列とインスタンス(PrintStream)が積んであるので、それを使ってこのメソッドを呼び出している。

戻り値がVなので、これでオペランドスタックは空になる。

 

B1の説明

B1 = returnのOPCODE

この時、オペランドスタックは既に空だが、returnにはオペランドスタックを捨てる作用もある。

 

アセンブラを知る人はこれらの命令の形式がアセンブラに近いものであることに気付くだろう。JITでNativeコードにするのに、この形式は非常に良いのだと思う。もちろん、JITが出るまえからこういう形式なんだろうけど。

 

LineNumberTableとPC number

PC numberは各Instructionに振られる番号で、LineNumberTableはPC numberとソースでの行番号をマッピングしている。実行時にソース行は意味を持たないためコンパイラオプションで消せる。完全にソース行と一致するわけではない。ほかにPC Numberで触れておきたいのは、今回でてこなかったException Tableでは、どのPC numberの区間で、どんな例外が起きた場合に、どのPC Numberへジャンプするのかが書かれたものということだ。

 

今回でてきたOPCODEのみでは実際のものを読むには足らない。そこは地道に調べて覚えていくしかない。OPCODEやInstructionやmnemonic(ニーモニック)という単語を使うといいだろう。