Class文件

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

  • Java虚拟机在不同平台上有不同的版本,但是他们都能执行同一class文件。
  • 任何编程语言的源代码,只要能编译成class文件,都能运行在Java虚拟机上面。例如Groovy语言。

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

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

  • 字面量。底层的数据类型,包括数字常量CONSTANT_Integer、CONSTANT_Float、CONSTANT_Long以及CONSTANT_Double和字符串常量CONSTANT_Utf8。它们的值就存储在各自的info[]之中。
  • 符号引用。包括类和接口名、字段和方法信息等。他们都通过索引直接或间接地指向CONSTANT_Utf8常量。

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引用都含有:

  • class_index。指向CONSTANT_Class_info常量,代表该field或method所在的类,或者interface method所在的接口。
  • name_and_type_index。指向CONSTANT_NameAndType常量。

所以,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];
}
  • access_flags,字段访问标志
  • name_index,字段名称,再次无情指向常量池中的CONSTANT_Utf8。
  • descriptor_index,字段类型,同样指向常量池中的CONSTANT_Utf8。
  • attributes_count,属性数组的长度。一个字段可能拥有一些属性,用于存储额外信息,如初始化值、注释信息等。
  • 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];
}

其中:

  • attribute_name_index,指向常量池中的CONSTANT_Utf8_info常量,表示该属性的类型(如表2所示)。
  • attribute_length,指出下一项info[]的长度。
  • 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[]内容非常丰富:

  • max_stack,方法执行过程中,操作数栈不停地变化,在整个执行过程中存在着一个最大深度,这是在编译过程中决定的。
  • max_locals,局部变量表的最大size。相比于操作数栈,局部变量表像一个数组。
  • code[code_length],这个就是方法最重要的字节码咯,jvm把它翻译成一个又一个的jvm指令,然后就可以执行方法了。
  • exception_table,异常表,表中每一项是由start_pc、end_pc、handler_pc以及catch_type组成的结构体那样的东东。从方法字节码的start_pc偏移量开始到end_pc偏移量为止的这段代码中,如果遇到catch_type所指向的异常,那么代码就跳转到handler_pc的位置执行,说白了,就是写代码的try-catch结构。
  • attributes[attributes_count],Code属性的属性。从表2中可以看出Code属性可以拥有包括StackMapTable、LineNumberTable、LocalVariableTable以及LocalVariableTypeTable在内的4中属性。其中LineNumberTableLocalVariableTable以及LocalVariableTypeTable三个与类的SourceFile属性一起是调试时使用的。比如,LineNumberTable用来指定字节码与源文件中的行号的对应关系。 StackMapTable是用来class文件的类型检验。这4个属性都不是运行时必需的。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容