`

HelloWorld.class 文件的解读

    博客分类:
  • JVM
阅读更多

这一篇主要针对最简单的HelloWorld的class文件进行分析,按照上一篇文章的结构去实例化的分析一个class文件。

下面是java源文件

 

 

public class HelloWorld{
	public static void main(String [] arvgs){
	  System.out.println("hello world");
}
}

 

 运行 javac HelloWorld.java 得到的class 文件如下:

 

00000000h: CA FE BA BE 00 00 00 2E 00 1D 0A 00 06 00 0F 09 ; 漱壕............
00000010h: 00 10 00 11 08 00 12 0A 00 13 00 14 07 00 15 07 ; ................
00000020h: 00 16 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 ; .....<init>...()
00000030h: 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E ; V...Code...LineN
00000040h: 75 6D 62 65 72 54 61 62 6C 65 01 00 04 6D 61 69 ; umberTable...mai
00000050h: 6E 01 00 16 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 ; n...([Ljava/lang
00000060h: 2F 53 74 72 69 6E 67 3B 29 56 01 00 0A 53 6F 75 ; /String;)V...Sou
00000070h: 72 63 65 46 69 6C 65 01 00 0F 48 65 6C 6C 6F 57 ; rceFile...HelloW
00000080h: 6F 72 6C 64 2E 6A 61 76 61 0C 00 07 00 08 07 00 ; orld.java.......
00000090h: 17 0C 00 18 00 19 01 00 0B 68 65 6C 6C 6F 20 77 ; .........hello w
000000a0h: 6F 72 6C 64 07 00 1A 0C 00 1B 00 1C 01 00 0A 48 ; orld...........H
000000b0h: 65 6C 6C 6F 57 6F 72 6C 64 01 00 10 6A 61 76 61 ; elloWorld...java
000000c0h: 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 01 00 10 6A ; /lang/Object...j
000000d0h: 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D 01 ; ava/lang/System.
000000e0h: 00 03 6F 75 74 01 00 15 4C 6A 61 76 61 2F 69 6F ; ..out...Ljava/io
000000f0h: 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B 01 00 13 ; /PrintStream;...
00000100h: 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 ; java/io/PrintStr
00000110h: 65 61 6D 01 00 07 70 72 69 6E 74 6C 6E 01 00 15 ; eam...println...
00000120h: 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 ; (Ljava/lang/Stri
00000130h: 6E 67 3B 29 56 00 21 00 05 00 06 00 00 00 00 00 ; ng;)V.!.........
00000140h: 02 00 01 00 07 00 08 00 01 00 09 00 00 00 1D 00 ; ................
00000150h: 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01 ; .......*?.?...
00000160h: 00 0A 00 00 00 06 00 01 00 00 00 01 00 09 00 0B ; ................
00000170h: 00 0C 00 01 00 09 00 00 00 25 00 02 00 01 00 00 ; .........%......
00000180h: 00 09 B2 00 02 12 03 B6 00 04 B1 00 00 00 01 00 ; ..?...?.?....
00000190h: 0A 00 00 00 0A 00 02 00 00 00 03 00 08 00 04 00 ; ................
000001a0h: 01 00 0D 00 00 00 02 00 0E                      ; .........

 

 

接下去开始分析每一个自己的含义:

 

  1.  0X CA FE BA BE  四个字节:表示魔数(magic),作用在于区分是否是class 文件;
  2.  0X 00 00 两个字节:表示此版本号(minor_version),
  3.  0X 00 2E 两个字节:表示主版本号(major_version),可以发现jdk 1.6.0_24 的主版本号为 46,

  4.  0X 00 1D 两个字节:表示常量池的个数(constant_pool_count),这里说明共有28个常量(要减1),常量池列表的索引从1开始,没有索引为0的常量,但是0索引也被计数在数量中,所有需要减1;接下去开始是常量池的信息了,还记得cp_info 的结构吗?第一个字节为tag,后面根据tag值不一样而不一样;

  5. 第一个常量:tag=0X 0A 为一个 constant_methodref 类型(对一个类中申明的方法的符号引用),根据它的定义,后面四个字节属于它,class_index=0X00 06,name_and_type_index=0X00 0F ; 
         (1)、首先看class_index,顾名思义,表示这个方法所属于的类在常量池中的索引,查看第6号常量池,是一个constant_class,正是我们想要的,根据constant_class的定义;跟踪到22号常量池,可以看到这个方法属于Object类;
          (2)、第二个name_and_type_index:表示这个方法所对应的name_and_type类型在常量池中的索引;查看第15号常量池,正好是我们需要的constant_namdAndType类型的定义,继续查看constant_namdAndType的定义,发现名字和描述符,分别指向第7号和8号常量池,即 void init();方法。

  6. 第二个常量:tag=0X 09为一个constant_Fieldref 类型(对一个字段的符号的引用),根据它的定义后面四个字节属于它,class_index=0X00 10 , name_and_type_index = 0X 00 11
         (1)、同样先看class_index:表示这个字段所属的类,跟踪到16号常量池为constant_Class类型,并且指向第23号常量池,看23号常量池,表示这个字段属于 java/lang/System方法;
         (2)、name_and_type_index :表示name_and_type类型在常量池中的索引;查看17号常量池为constant_namdAndType类型,指向24、25号常量,表示字段名字为out,类型为 Ljava/io/PrintStream
     
  7. 第三个常量:tag=0X 08, 为一个constant_String 类型(String 类型的字符串),根据它的定义后面的两个字节属于它,String_index=0X00 12,查看第18号常量池 为“hello world” 正是我们想要的输出的内容;

  8. 第四个常量:tag=0X 0A, 为一个 constant_methodref 类型(对一个类中申明的方法的符号引用),根据它的定义,后面四个字节属于它,class_index=0X00 13,name_and_type_index=0X00 14
         (1)、首先看class_index,顾名思义,表示这个方法所属于的类在常量池中的索引,查看第19号常量池,是一个constant_class,正是我们想要的,根据constant_class的定义;跟踪到26号常量池,可以看到这个方法属于java/io/PrintStream类;
          (2)、第二个name_and_type_index:表示这个方法所对应的name_and_type类型在常量池中的索引;查看第20号常量池,正好是我们需要的constant_namdAndType类型的定义,继续查看constant_namdAndType的定义,发现名字和描述符,分别指向第27号和28号常量池,即 void println( (Ljava/lang/String;)方法。

  9. 第五个常量:tag=OX 07,为一个constant_Class类型(对类或者接口的符号引用),根据它的定义后面的2个字节属于它,name_index=0X00 15,查看第21号常量,为HelloWorld ,表示我们写的类HelloWorld;

  10. 第六个常量:tag=0X 07,为一个constant_Class类型(对类或者接口的符号引用),根据它的定义后面的2个字节属于它,name_index=0X00 16,查看第22号常量,为 java/lang/Object ,表示object类;

  11. 第七个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 06 表示后面有6个字节属于它的内容:bytes=0X 3C 69 6E 69 74  3E 即是:<init>;由前面可以知道为object的intit方法;

  12. 第八个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 03 表示后面有3个字节属于它的内容:bytes=0X 28 29 56  即是: ()V;由前面可以知道,这个表示object的intit方法的描述符:表示没有参数,返回值为void;

  13. 第九个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 04 表示后面有4个字节属于它的内容:bytes=0X  43 6F 64 65  即是: Code, 

  14. 第10个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 0F 表示后面有15个字节属于它的内容:bytes=0X 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65  即是: LineNumberTable  ; 

  15. 第11个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 04表示后面有4个字节属于它的内容:bytes=0X 6D 61 69 6E 即是: main ;表示main()方法;

  16. 第12个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 16表示后面有22个字节属于它的内容:bytes=0X 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72  69 6E 67 3B 29 56   即是:  ([Ljava/lang/String;)V;有前面可以这个这个表示main方法的描述符,即返回值为void,参数为 string[] ([Ljava/lang/String; 中的第一个[表示一维数组),

  17. 第13个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 0A表示后面有10个字节属于它的内容:bytes=0X 53 6F 75 72 63 65 46 69 6C 65              即是: SourceFile ;

  18. 第14个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 0F表示后面有15个字节属于它的内容:bytes=0X  48 65 6C 6C 6F 57 6F 72 6C 64 2E 6A 61 76 61  即是: HelloWorld.java,可以知道这个是我们的文件名字;

  19. 第15个常量:tag=0X 0C,为一个constant_namdAndType类型(字段方法的部分符号引用),根据它的定义后面的4个字节属于它,name_index=0X00 07,descriptor_index=00 08;由前面可以知道,这个是对object的intit方法的总体描述,包含名字和描述符;

  20. 第16个常量:tag=0X 07,为一个constant_Class类型(对类或者接口的符号引用),根据它的定义后面的2个字节属于它,name_index=0X00 17,查看23号常量池,表示java/lang/System类;

  21. 第17个常量:tag=0X 0C,为一个constant_namdAndType类型(字段方法的部分符号引用),根据它的定义后面的4个字节属于它,name_index=0X00 18,descriptor_index=00 19,由前面可以知道,这个是对 Ljava/io/PrintStream类型的out字段的描述;

  22. 第18个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 0B表示后面有11个字节属于它的内容:bytes=0X 68 65 6C 6C 6F 20 77 6F 72 6C 64         即是: hello world,我们想要输出的常量

  23. 第19个常量:tag=0X 07,为一个constant_Class类型(对类或者接口的符号引用),根据它的定义后面的2个字节属于它,name_index=0X00 1A,查看26号常量池,表示java/io/PrintStream类;

  24. 第20个常量:tag=0X 0C,为一个constant_namdAndType类型(字段方法的部分符号引用),根据它的定义后面的4个字节属于它,name_index=0X00 1B,descriptor_index=00 1C,查看27、28号常量,是对println  方法的描述,表示返回值为void,参数为一个String;

  25. 第21个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 0A表示后面有10个字节属于它的内容:bytes=48 65 6C 6C 6F 57 6F 72 6C 64  
     即是: HelloWorld ;表示我们的类名
           
  26. 第22个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 10表示后面有16个字节属于它的内容:bytes= 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74  即是:  java/lang/Object , 

  27. 第23个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 10表示后面有16个字节属于它的内容:bytes=  6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D  即是:  java/lang/System

  28. 第24个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 03表示后面有3个字节属于它的内容:bytes=   6F 75 74    即是:out 

  29. 第25个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 15表示后面有21个字节属于它的内容:bytes=  4C 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B    即是: Ljava/io/PrintStream;

  30. 第26个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 13表示后面有19个字节属于它的内容:bytes=6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72  65 61 6D    即是:  java/io/PrintStream

  31. 第27个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 07表示后面有7个字节属于它的内容:bytes=00000116h: 70 72 69 6E 74 6C 6E   即是: println   

  32. 第28个常量:tag=0X 01,为一个constant_UTF8类型(utf8编码的字符串),根据它的定义后面的长度可变,length=0X00 15表示后面有7个字节属于它的内容:bytes= 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69  6E 67 3B 29 56 即是:  (Ljava/lang/String;)V,至此常量池的解析就结束了。

  33. 0X 00 21 两个字节: 表示 access_flags,这里为0x0021。根据access_flags表可以查到,该值是0x00200x0001两者的和,即该类的修饰符为ACC_PUBLIC+ACC_SUPER,前者表示该类是public类型,后者表示采用invokespecial指令特殊处理对超类的调用

  34. 0X 00 05 两个字节:表示this_class;它是一个对常量池表项的索引,指向5号常量池;查看5号常量池正是HelloWorld

  35.  0X 00 06 两个字节:表示 super_class:它也是一个对常量池表象的索引,指向6号常量池;表示为object类;

  36. 0X 00 00 两个字节:表示interfaces_count,表示直接实现或者该解决扩展的超接口为0个;没有实现任何接口

  37. 0X 00 00 两个字节:表示feild_count,表示该类没有申明字段;

  38. 0X 00 02 两个字节:表示method_count,表示该类申明了2个方法,接下去是这两个方法的相信信息

  39. 第一个method_info的结构:根据上一章节的内容,接下去的2个字节表示
     (1)、access_flags=0X 00 01,表示是一个ACC_PUBLIC 的方法,
     (2)、在两个字节0X 00 07 表示 name_index,表示常量池中第7个常量为<init>,即是 <init>方法,
     (3)、在接下两个字节 0X00 08 表示desciptor_index,表示常量池第8个常量为 ()V ,即是没有参数,返回值为void;,
     (4)、在接下去两个字节0X 00 01 表示attribute_count,表示有1个attribute,
     (5)、接下去表示一个attribute_info 的结构;查看attribute_info 的结构定义:

  40. attribute_info {        
     u2 attribute_name_index;
     u4 attribute_length;
     u1 info[attribute_length];
        }
       1)、所以 在接下去的两个字节 0X 00 09,查看第9好常量池为Code,然后code_attribute的定义:


    Code_attribute {        
            u2 attribute_name_index;
             u4 attribute_length;
             u2 max_stack;
             u2 max_locals;
             u4 code_length;
             u1 code[code_length];
             u2 exception_table_length;
             {       u2 start_pc;
                    u2 end_pc;
                    u2 handler_pc;
                    u2 catch_type;
             }       exception_table[exception_table_length];
             u2 attributes_count;
             attribute_info attributes[attributes_count];
        }
         
    在看这个结构体 attribute_name_index =0X 00 09,然后4个字节0X 00 00 00 1D表示长度 为29个字节说明接下去的29个字节属于这个属性,这里不暂时不展开。 

  41. 第二个method_info的结构:去掉前面的29个字节,
    (1)、接下去的2个字节表示access_flags=0X 00 09,表示是一个ACC_PUBLIC和ACC_STATIC 的方法,
    (2)、在两个字节0X 00 0B表示 name_index,表示常量池中第11个常量为main ,即是 main 方法,
    (3)、在接下两个字节 0X00 0C 表示desciptor_index,表示常量池第12个常量为([Ljava/lang/Str ing;)V,即是参数为String [],返回值为void;
    (4)、在接下去两个字节0X 00 01 表示attribute_count,表示有1个attribute,索引接下去表示一个attribute_info 的结构;所有查看attribute_info 的结构定义:
     
    attribute_info {        
     u2 attribute_name_index;
     u4 attribute_length;
     u1 info[attribute_length];
        }
     1)、所以 在接下去的两个字节 0X 00 09,查看第9好常量池为Code,然后code_attribute的定义:

    Code_attribute {
             u2 attribute_name_index;
             u4 attribute_length;
             u2 max_stack;
             u2 max_locals;
             u4 code_length;
             u1 code[code_length];
             u2 exception_table_length;
             {       u2 start_pc;
                    u2 end_pc;
                    u2 handler_pc;
                    u2 catch_type;
             }       exception_table[exception_table_length];
             u2 attributes_count;
             attribute_info attributes[attributes_count];
        }
     在看这个结构体 attribute_name_index =0X 00 09,然后4个字节0X 00 00 00 25表示长度 为37个字节说明接下去的37个字节属于这个属性,这里不暂时不展开。


  42. 去掉前面的37个字节,接下去的两个字节0X 00 01 表示接下去有一个attributes_info结构,参照前面的定义,(1)、0X 00 0D表示 attribute_name_index,13号常量为 SourceFile,表示属性名字为SourceFile; 该属性是一个可选的定长属性,并且在class文件中只有一个sourcefile_atrribute存在,对于给定的ClassFile结构的attributes列表中不能有多于一的SourceFile属性;查阅SourceFile_attribute表可知,
    sourceFile_attribute{
       U2  attribute_name_index ;
       U4  attribute_length;
       U2   sourcefile_index;
    }
     
     (1)、下面的
    4个字节为attribute_length项,其值为0x00 00 00 02,它表示在该项后面还有2个字节的信息,这个长度永远为2。
      (2)、根据SourceFile_attribute表,最后的这两个字节是sourcefile_index项,该项的值是一个对CONSTANT_Utf8_info结构的常量池表项的索引,其信息表示的是该Class文件的源文件名称。在这里值为0x00 0E,14号常量池表项存储的信息可解析为“HelloWorld.java”,这是该Class文件的源文件名称(不包括路径)

到此整个 HelloWord.class 解析结束。。下一节。我们将通过几个例子说明怎么利用读懂的class文件的格式。

 

 

本站支持 pay for your wishes

4
0
分享到:
评论

相关推荐

    HelloWorld.class

    HelloWorld.class

    helloworld.war镜像制作文件

    docker镜像基础: helloworld.war包镜像(上)软件包 helloworld.war jdk-linux-x64.tar.gz apache-tomcat-8.0.53.tar.gz

    HelloWorld.sln

    Helloworld,HelloWorld.sln

    helloworld.bin

    helloworld.bin

    HelloWorld.java

    HelloWorld.java

    Helloworld.ts

    Helloworld.ts

    HelloWorld.PCL3GUI打印机测试文件

    gs -sDEVICE=ijs -sIjsServer=hpijs -dIjsUseOutputFD -sDeviceManufacturer="HEWLETT-PACKARD" -sDeviceModel="deskjet 5550" -r300x300 -dNOPAUSE -dSAFER -sstdout=%stderr -sOutputFile=%stdout ~/HelloWorld....

    HelloWorld.vue

    本文件是flv+express+ffmpeg脚手架,vue项目中的HelloWorld.vue。如需要下载另外一个express(index.js)文件请查看我的资源。自行下载。代码的使用,具体看文件中的注释。 单独的文件是不能运行的,生成对应的项目...

    helloworld.war

    Manning.Struts.2.in.Action 's example HelloWorld.war provides a convenient example of a minimal Struts 2 application

    helloworld.rar

    CCS3.3开发环境+TMS320F2812模块pakage包具体安装方法及使用方法可以点击我头像参考我的DSP开发Hello World博客,此处为个人的DSP的hello world代码

Global site tag (gtag.js) - Google Analytics