2、java 字节码文件解析

classFile的文件结构:

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];
}


写一段代码:

package jvm;

public class ClassFile {
}


然后 用查看二进制文件的工具:

CA FE BA BE 00 00 00 34 00 10 0A 00 03 00 0D 07 00 0E 07 00 0F 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01 00 04 74 68 69 73 01 00 0F 4C 6A 76 6D 2F 43 6C 61 73 73 46 69 6C 65 3B 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 00 0E 43 6C 61 73 73 46 69 6C 65 2E 6A 61 76 61 0C 00 04 00 05 01 00 0D 6A 76 6D 2F 43 6C 61 73 73 46 69 6C 65 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 00 21 00 02 00 03 00 00 00 00 00 01 00 01 00 04 00 05 00 01 00 06 00 00 00 2F 00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 02 00 07 00 00 00 06 00 01 00 00 00 03 00 08 00 00 00 0C 00 01 00 00 00 05 00 09 00 0A 00 00 00 01 00 0B 00 00 00 02 00 0C


开头四个字节是CAFEBABE,这个是魔数,用于标识这个文件能被jvm识别。接着2个字节是次版本号,后续0x0034表示主版本号,52代表jdk8。(jdk1用45表示,后面每增加一个版本,就加1)

接着是0x0010=16,0用于保留,1 - 15表示有15个常量。接着:就是各个常量的解析了。每个常量的大小是不定的。


常量池的解析:

首先给出常量池中的tag类型:

Table 4.3. Constant pool tags
    
常量类型           常量值
Constant Type    Value
    
CONSTANT_Class  7
CONSTANT_Fieldref   9
CONSTANT_Methodref  10
CONSTANT_InterfaceMethodref 11
CONSTANT_String 8
CONSTANT_Integer    3
CONSTANT_Float  4
CONSTANT_Long   5
CONSTANT_Double 6
CONSTANT_NameAndType    12
CONSTANT_Utf8   1
CONSTANT_MethodHandle   15
CONSTANT_MethodType 16
CONSTANT_InvokeDynamic  18

第一个常量: 然后就到了0x0A = 10,查找上述的表格为:CONSTANT_Methodref

CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}

其中第一个已经被用了,然后就是两个字节来表示class_index:对应的值为:0x0003,这个是表示常量池中的第3个变量。然后是0x00 0D表示常量池的第13个常量。

第二个常量: 然后就是一个字节:表示tag: 0x07。找到对应的CONSTANT_Class

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

然后是两个字节的name_index:

0x00 0E,表示第14个常量。

第三个常量: 先是tag,占了一个字节 0x07,两个字节的name_index => 即0x00 0F,表示第15个常量的位置

第四个常量:先是tag0x01,CONSTANT_Utf8,结构为:

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

两个字节表示长度: 0x00 06,表示这个字符常量占六个字节:即: 3C 69 6E 69 74 3E

用16进制转字符:表示

第五个常量:显示tag=0x01,表示CONSTANT_Utf8,接着两个字节为:0x00 03,表示这个字符占三个字节:

28 29 56 表示: ()V

第六个常量:tag=1,长度为4:43 6F 64 65 ==> Code

第七个常量:tag=1,长度等于15:4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 ==》 LineNumberTable

第8个常量:tag =1,长度等于18:4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 - LocalVariableTable

第9个常量:tag =1,长度等于4,74 68 69 73  =》  this

第10个常量:tag = 1,长度:00 0F等于15,4C 6A 76 6D 2F 43 6C 61 73 73 46 69 6C 65 3B   ==》  Ljvm/ClassFile;

第11个常量:tag = 1,长度: 00 0A 等于10, 53 6F 75 72 63 65 46 69 6C 65   =》  SourceFile

第12个常量: tag = 1,长度: 00 0E 等于14,43 6C 61 73 73 46 69 6C 65 2E 6A 61 76 61 ==》 ClassFile.java

第13个常量:tag = 0x0C,等于12,CONSTANT_NameAndType

CONSTANT_NameAndType_info {
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}

接着两个字节是: 00 04 表示第四个,

00 05,第五个常量。

第14个常量:tag =1 , 00 0D,等于13,6A 76 6D 2F 43 6C 61 73 73 46 69 6C 65  ==》 jvm/ClassFile

第15个常量: tag =1 ,00 10 等于16,6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74  ==》 java/lang/Object

到此为止,所有的常量都解析完了。


接下来两个字节就表示访问标记:00 21  表示。类的访问标致表:

Table 4.1-A. Class access and property modifiers

Flag Name	Value	Interpretation
ACC_PUBLIC	0x0001	Declared public; may be accessed from outside its package.
ACC_FINAL	0x0010	Declared final; no subclasses allowed.
ACC_SUPER	0x0020	Treat superclass methods specially when invoked by the invokespecial instruction.
ACC_INTERFACE	0x0200	Is an interface, not a class.
ACC_ABSTRACT	0x0400	Declared abstract; must not be instantiated.
ACC_SYNTHETIC	0x1000	Declared synthetic; not present in the source code.
ACC_ANNOTATION	0x2000	Declared as an annotation type.
ACC_ENUM	0x4000	Declared as an enum type.

可以看出这里有两个属性: ACC_PUBLIC    ACC_SUPER    表示公有的,当由invokespecial指令调用时,要特别对待超类方法。

接下来两个字节表示:当前类名的索引:00 02表示常量池第二个,查看为:jvm/ClassFile

接下来两个字节表示:父类类名的索引:00 03表示常量池第三个,查看为:java/lang/Object

接下来两个字节表示:接口的数量,0000,表示没有实现接口。如果这个为0,那么接下来的接口interface_info,就不存在了。

接下来两个字节表示:  成员表里的数量,0000,表示没有成员变量。那么接着的field_info也不存在了。

接下来两个字节表示,方法的个数,0001表示有一个方法,接下来时对应method_info的信息。

method_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

接下来两个字节表示方法的访问标志:方法访问标致表如下:

Table 4.6-A. Method access and property flags

Flag Name	Value	Interpretation
ACC_PUBLIC	0x0001	Declared public; may be accessed from outside its package.
ACC_PRIVATE	0x0002	Declared private; accessible only within the defining class.ACC_PROTECTED	0x0004	Declared protected; may be accessed within subclasses.
ACC_STATIC	0x0008	Declared static.
ACC_FINAL	0x0010	Declared final; must not be overridden (§5.4.5).
ACC_SYNCHRONIZED	0x0020	Declared synchronized; invocation is wrapped by a monitor use.
ACC_BRIDGE	0x0040	A bridge method, generated by the compiler.
ACC_VARARGS	0x0080	Declared with variable number of arguments.
ACC_NATIVE	0x0100	Declared native; implemented in a language other than Java.
ACC_ABSTRACT	0x0400	Declared abstract; no implementation is provided.
ACC_STRICT	0x0800	Declared strictfp; floating-point mode is FP-strict.
ACC_SYNTHETIC	0x1000	Declared synthetic; not present in the source code.

00 01 表示:ACC_PUBLIC 表示公有的访问权限。

接下来是名字的索引: 00 04表示常量池里的第四个常量。即

接下来是描述符的索引: 00 05 表示常量池第五个常量:()V这个表示空参数,空返回值

然后两个字节是属性的个数:0001表示有一个属性:属性表如下:

attribute_info {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 info[attribute_length];
}

接下来两个字节0006表示,属性名的索引是常量池的第6个常量,为Code

接下来四个字节表示属性长度:00 00 00 2F = 47

Co的结构为:

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];
}

接下47个字节就是Code里面的内容:

00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 02 00 07 00 00 00 06 00 01 00 00 00 03 00 08 00 00 00 0C 00 01 00 00 00 05 00 09 00 0A 00 00

接下来是两个字节到max_stack=0x0001表示最大的栈深度为1

接下来表示本地变量:max_locals,表示有一个本地变量,这里明明没有本地变量的,为什么是1呢?其实是java中,每个方法都会有this这个本地变量。

接下来是四个字节表示存放code信息的数组长度:00000005,表示五个字节:即: 2A B7 00 01 B1

接下来两个字节表示异常表的长度:0000表示0,没有。

接下两个字段是属性的个数:0002表示有两个属性。

然后是两个字节的属性索引位置:0007表示常量池第7个常量,即: LineNumberTable,这个是记录源码行号的表。

然后是四个字节的属性长度:00 00 00 06表示属性长度为6,即:00 01 00 00 00 03

然后两给字节,表示第二个变量的索引位置:0008,即LocalVariableTable,用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系,非运行时必须属性。

接下来四个字节:00 00 00 0C表示属性的长度为12,即:00 01 00 00 00 05 00 09 00 0A 00 00

至次,Code属性已经完结。

接下来两个属性表示:属性的个数: 0001,表示有一个属性,然后就是两个字节表示属性名在常量池中的索引位置:00 0B = 11,即SourceFile

接着四个字节表示这个属性的长度:  00 00 00 02 = 2,表示属性长度为2,即:00 0C

到此,字节码全部解析完成。


同样可以通过jclasslib插件来查看字节码的信息。可以在idea中安装这个插件,然后点开编译出来的.class文件,然后edit,有个show bytecode with jclasslib的按钮。


也可以通过javap -verbose ClassFile.class 可以反编译出对应的字节码文件。

public class jvm.ClassFile
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#13         // java/lang/Object."":()V
   #2 = Class              #14            // jvm/ClassFile
   #3 = Class              #15            // java/lang/Object
   #4 = Utf8               
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               LocalVariableTable
   #9 = Utf8               this
  #10 = Utf8               Ljvm/ClassFile;
  #11 = Utf8               SourceFile
  #12 = Utf8               ClassFile.java
  #13 = NameAndType        #4:#5          // "":()V
  #14 = Utf8               jvm/ClassFile
  #15 = Utf8               java/lang/Object
{
  public jvm.ClassFile();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ljvm/ClassFile;
}


你可能感兴趣的:(2、java 字节码文件解析)