Class文件

2017-05-12  本文已影响0人  趁现在赶快回家

Java的跨平台特性建立在Java虚拟机之上。

1. Class文件结构

Class文件在Java虚拟机规范中的定义如下:

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 Class文件总体结构<br>摘自《实战Java虚拟机:JVM故障诊断与性能优化》 ——葛一鸣

下面来逐一说明Class文件中的每一种结构。

1.1 常量池

常量池中有很多种常量,有一种可以通用来描述它们的结构:

cp_info {
    u1 tag;
    u1 info[];
}

每一种常量都以一个描述当前常量类型的u1(8字节无符号)整数tag开始,后面接上根据不同常量类型变化的2个或多个字节组成的,用来描述该常量携带的具体信息的东东。比如CONSTANT_Class_info常量

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

它的info[]就是u2(2字节无符号整数)表示的name_index。而CONSTANT_Integer_info的info[]则是u4(4字节无符号整数)表示的整数具体值:

CONSTANT_Integer_info {
    u1 tag;
    u4 bytes;
}

截止到Java7,常量池包含了14种常量,他们各自的tag值如下表:

<h6 align = "center">表1 常量类型及其tag值</h6>

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

总的来说,常量池中的常量包括字面量和符号引用两类

1.1.1 CONSTANT_Class

The CONSTANT_Class_info structure is used to represent a class or an interface:

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}

CONSTANT_Class类型的常量,通过name_index指向常量池中的一个CONSTANT_Utf8常量,用来表示自己所代表的类或接口名。

由于数组同样是对象,CONSTANT_Class表示数组的方式与方法与域的描述符中数组表示方式一样,如int[][]表示成[[I,Thread[]表示成[Ljava/lang/Thread。

1.1.2 CONSTANT_NameAndType

The CONSTANT_NameAndType_info structure is used to represent a field or method, without indicating which class or interface type it belongs to:

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

1.1.3 CONSTANT_Fieldref, CONSTANT_Methodref, and CONSTANT_InterfaceMethodref

分别表示Fields, methods, and interface methods引用,结构相似:

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

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

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

由于CONSTANT_Class和CONSTANT_NameAndType可以完全确定一个字段或者方法,所以Fields, methods, and interface methods引用都含有:

所以,CONSTANT_Fieldref, CONSTANT_Methodref, and CONSTANT_InterfaceMethodref与CONSTANT_Class和CONSTANT_NameAndType以及CONSTANT_Utf8的引用关系大致如下:

图2 常量引用关系<br>摘自 《自己动手写Java虚拟机》——张秀宏

常量池疑问

  1. 不太明白的是,为什么有了CONSTANT_Utf8还要CONSTANT_String,其实CONSTANT_String也是指向CONSTANT_Utf8的啊?
  2. CONSTANT_MethodHandle、CONSTANT_MethodType、CONSTANT_InvokeDynamic是Java7新加入的,它们用来干啥?我还没学到这儿来……

1.2 字段与方法

如图1所示,类的字段与方法具有相似的结构。以字段为例,字段包括字段数目以及字段具体信息数组:

u2             fields_count;
field_info     fields[fields_count];

如图1中看到的那样,字段具体信息的具体结构:

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

字段疑问

  1. fields与常量池中的CONSTANT_Fieldref的关系?
  2. 看到field的属性中也许带有初始化值,突然想到《Java编程思想》中的一道题,定义2个String类型的字段,问在定义的时候初始化与在构造函数中初始化有什么不同?
    我当时编写了这样的程序:
public class Task2 {
        public String s1="has initialized";
        public String s2;
        public Task2(){
            s2="initialized in constructor";
        }
    }

编译之后,通过javap -c Task2 查看:

      Compiled from "Task2.java"
      public class peris.sky.learn.Task2 {
      public java.lang.String s1;

      public java.lang.String s2;

       public peris.sky.learn.Task2();
          Code:
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: aload_0
             5: ldc           #2                  // String has initialized
             7: putfield      #3                  // Field s1:Ljava/lang/String;
            10: aload_0
            11: ldc           #4                  // String initialized in constructor
            13: putfield      #5                  // Field s2:Ljava/lang/String;
            16: return
      }

发现,s1与s2的初始化其实都是在构造函数中完成的,只是先后顺序不一样。
学了class文件的字段之后,利用classpy查看class文件,发现s1与s2的属性表中,并没有初始化值,于是仔细阅读java虚拟机规范,发现这个初始化值只有常量(static and final)类型的field的属性表中才有。

1.3 属性

属性出现在class文件中的类属性表、字段属性表、方法属性表以及方法中的Code属性的属性表四种地方。属性与常量池中的常量类似,有多种类型,但都有相似的结构:

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

其中:

<h6 align = "center">表2 属性类型、其可能出现的位置以及含义</h6>

AttributeType classfile field_info method_info Code 含义
ConstantValue y 常量字段的值
Code y 方法执行的字节码
StackMapTable y 类型检查的时候用
Exceptions y 方法可能抛出的一样信息
InnerClasses y 内部类
EnclosingMethod y
Synthetic y y y 源文件中不存在的类成员
Signature y y y
SourceFile y 源文件名
SourceDebugExtension y
LineNumberTable y 方法的行号
LocalVariableTable y 方法的局部变量信息
LocalVariableTypeTable y
Deprecated y y y 指出不建议使用的东东
RuntimeVisibleAnnotations y y y
RuntimeInvisibleAnnotations y y y
RuntimeVisibleParameterAnnotations y
RuntimeInvisibleParameterAnnotations y
AnnotationDefault y
BootstrapMethods y

1.3.1 Code属性

Code只存在于method_info之中,Code属性中存放字节码等方法相关信息,正如前面所说的那样,Code属性中还可以有其它属性,所以比较麻烦。它的结构如下:

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

可以看出,Code的info[]内容非常丰富:

上一篇 下一篇

猜你喜欢

热点阅读