Class类文件的结构

我们从头到尾将Class类文件的结构过一遍。
注意:该结构没有任何分隔符,按照严格的顺序限定,由1到n,由n到m,都有其定义。

  • 魔术Magic
类型 位置 数量 值(十六进制)
u4 1-4字节 1 0xCAFEBABE

据Java开发小组最初的关键成员Patrick Naughton所说“选择0xCAFEBABE是因为它象征着著名咖啡品牌Peet's Coffee中深受欢迎的Baristas咖啡”,这个魔术似乎预示着日后“Java”商标的出现。

  • 次版本号Minor Version
类型 位置 数量 值(十六进制)
u2 5-6字节 1 非固定值
  • 主版本号Major Version
类型 位置 数量 值(十六进制)
u2 7-8字节 1 非固定值

每个JDK大版本发布主版本号向上加1,高版本的JDK能向下兼容以前版本的Class文件。虚拟机必须拒绝执行超过其版本号的Class文件。
例如,JDK1.1支持45.0至45.65535的Class文件,JDK1.2也能支持45.0至46.65535的Class文件。

为了方便理解,举个例子:
创建一个Test.java文件

class Test {
    public static void main(String[] args) {
        System.out.println("test");
    }
}

使用的javac版本号

$ javac -version
javac 1.8.0_161

编译该文件,生成Test.class文件,vim打开Test.class文件

$ javac Test.java
$ vim -b Test.class
^@^F^@^O        ^@^P^@^Q^H^@^R
^@^S^@^T^G^@^U^G^@^V^A^@^F<init>^A^@^C()V^A^@^DCode^A^@^OLineNumberTable^A^@^Dmain^A^@^V([Ljava/lang/String;)V^A^@
SourceFile^A^@  Test.java^L^@^G^@^H^G^@^W^L^@^X^@^Y^A^@^Dtest^G^@^Z^L^@^[^@^\^A^@^DTest^A^@^Pjava/lang/Object^A^@^Pjava/lang/System^A^@^Cout^A^@^ULjava/io/PrintStream;^A^@^Sjava/io/PrintStream^A^@^Gprintln^A^@^U(Ljava/lang/String;)V^@ ^@^E^@^F^@^@^@^@^@^B^@^@^@^G^@^H^@^A^@       ^@^@^@^]^@^A^@^A^@^@^@^E*·^@^A±^@^@^@^A^@
^@^@^@^F^@^A^@^@^@^A^@  ^@^K^@^L^@^A^@  ^@^@^@%^@^B^@^A^@^@^@   ²^@^B^R^C¶^@^D±^@^@^@^A^@
^@^@^@
^@^B^@^@^@^C^@^H^@^D^@^A^@^M^@^@^@^B^@^N

命令模式下输入
:%!xxd

00000000: ca fe ba be 00 00 00 34 00 1d 0a 00 06 00 0f 09  .......4........
00000010: 00 10 00 11 08 00 12 0a 00 13 00 14 07 00 15 07  ................
00000020: 00 16 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29  .....<init>...()
00000030: 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e  V...Code...LineN
00000040: 75 6d 62 65 72 54 61 62 6c 65 01 00 04 6d 61 69  umberTable...mai
00000050: 6e 01 00 16 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67  n...([Ljava/lang
00000060: 2f 53 74 72 69 6e 67 3b 29 56 01 00 0a 53 6f 75  /String;)V...Sou
00000070: 72 63 65 46 69 6c 65 01 00 09 54 65 73 74 2e 6a  rceFile...Test.j
00000080: 61 76 61 0c 00 07 00 08 07 00 17 0c 00 18 00 19  ava.............
00000090: 01 00 04 74 65 73 74 07 00 1a 0c 00 1b 00 1c 01  ...test.........
000000a0: 00 04 54 65 73 74 01 00 10 6a 61 76 61 2f 6c 61  ..Test...java/la
000000b0: 6e 67 2f 4f 62 6a 65 63 74 01 00 10 6a 61 76 61  ng/Object...java
000000c0: 2f 6c 61 6e 67 2f 53 79 73 74 65 6d 01 00 03 6f  /lang/System...o
000000d0: 75 74 01 00 15 4c 6a 61 76 61 2f 69 6f 2f 50 72  ut...Ljava/io/Pr
000000e0: 69 6e 74 53 74 72 65 61 6d 3b 01 00 13 6a 61 76  intStream;...jav
000000f0: 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d  a/io/PrintStream
00000100: 01 00 07 70 72 69 6e 74 6c 6e 01 00 15 28 4c 6a  ...println...(Lj
00000110: 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b  ava/lang/String;
00000120: 29 56 00 20 00 05 00 06 00 00 00 00 00 02 00 00  )V. ............
00000130: 00 07 00 08 00 01 00 09 00 00 00 1d 00 01 00 01  ................
00000140: 00 00 00 05 2a b7 00 01 b1 00 00 00 01 00 0a 00  ....*...........
00000150: 00 00 06 00 01 00 00 00 01 00 09 00 0b 00 0c 00  ................
00000160: 01 00 09 00 00 00 25 00 02 00 01 00 00 00 09 b2  ......%.........
00000170: 00 02 12 03 b6 00 04 b1 00 00 00 01 00 0a 00 00  ................
00000180: 00 0a 00 02 00 00 00 03 00 08 00 04 00 01 00 0d  ................
00000190: 00 00 00 02 00 0e 0a                             .......

我们观察到,第一行的前8个字节是:ca fe ba be 00 00 00 34
1-4字节是Magic,5-6字节是Minor Version,7-8字节是Major Version,主版本号0x0034的十进制是52,JDK1.8及以上支持52的Class文件。不同的JDK版本,编译生成的主次版本号不同(JDK版本对应支持Class版本可上网搜索)

  • 常量池

9-10字节代表常量池容量计数值(constant_pool_count)
我们看到,跟在ca fe ba be 00 00 00 34之后的是00 1d 0a 00 06 00 0f 09,0x001d的十进制是29,表示常量池中有28项常量,索引值1~28。注意:跟数组从0开始的习惯不同,0空出来的目的在于满足后面某些指向常量池的索引值的数据在特定情况下表示“不引用任何一个常量池项目”。

类型 标识 描述
Utf8 1 UTF-8编码字符串
Integer 3 整型字面量
Float 4 浮点型字面量
Long 5 长整型字面量
Double 6 双精度浮点型字面量
Class 7 类或接口的符号引用
String 8 字符串字面量
Fieldref 9 字段的符号引用
Methodref 10 类中方法的符合引用
InterfaceMethodref 11 接口中方法的引用
NameAndType 12 字段或方法部分符合引用
MethodHandle 15 方法句柄
MethodType 16 标识方法类型
InvokeDynamic 18 动态方法调用

  • String
type descriptor
u1 tag
u2 string_index

  • Methodref
type descriptor
u1 tag
u2 class_index
u2 name_and_type_index

  • 字段表集合

字段表(field_info)包括的信息:
字段的作用域修饰符:public、protected、private
实例变量or类变量修饰符:static
变量可变性修饰符:final
并发可见性修饰符:volatile
可否被序列化修饰符:transient
字段数据类型:基本类型、对象、数组
字段名称

上面的信息中,修饰符都是boolean值,要么0,要么1。
字段数据类型与字段名称就无法固定长度去表示。

Java中,两个字段的数据类型、修饰符不管是否相同,都必须使用不一样的名称。但是对字节码没这个要求,两个字段的描述符不一致,字段重名就是合法的。

  • 方法表集合

方法修饰符中,不包括volatile和transient。
相对的,新增synchronized、native、strictfp和abstract

  • 属性表集合
  1. Code属性
  2. Exception属性
  3. LineNumberTable属性
  4. LocalVariableTable属性
  5. LocalVariableTypeTable属性(引入泛型时新增的属性)
  6. SourceFile属性
  7. ConstantValue属性
  8. InnerClasses属性
  9. Deprecated及Synthetic属性
  10. StackMapTable属性
  11. Signature属性
  12. BootstrapMethods属性
  • 字节码指令简介
  1. 字节码与数据类型
    在Java虚拟机的指令集中,大多数指令都包含其操作所对应的数据类型信息,例如iload指令用于从局部变量中加载int型的数据到操作数栈中。
    在指令集中,load有操作int的指令iload,但对byte、char、short和boolean,没有对应的指令,在处理它们时,会先转换成int类型,然后使用iload进行操作。
  2. 加载和存储指令
    加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈之间来回传输。
    将一个局部变量加载到操作栈:iload、iload_<n>、lload、lload_<n>、fload、fload_<n>、dload、dload_<n>、aload、aload_<n>
    将一个数值从操作数栈存储到局部变量:istore、istore_<n>、lstore、lstore_<n>、fstore、fstore_<n>、dstore、dstore_<n>、astore、astore_<n>
    将一个常量加载到操作数栈:bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_ml、iconst_<i>、lconst<l>、fconst_<f>、dconst<d>
    扩充局部变量表的访问索引的指令:wide
  3. 运算指令
    加法指令:iadd、ladd、fadd、dadd
    减法指令:isub、lsub、fsub、dsub
    乘法指令:imul、lmul、fmul、dmul
    除法指令:idiv、ldiv、fdiv、ddiv
    求余指令:irem、lrem、frem、drem
    取反指令:ineg、lneg、fneg、dneg
    位移指令:ishl、ishr、iushr、lshl、lshr、lushr
    按位或指令:ior、lor
    按位与指令:iand、land
    按位异或指令:ixor、lxor
    局部变量自增指令:iinc
    比较指令:dcmpg、dcmpl、fcmpg、fcmpl、lcmp
  4. 类型转换指令
    宽化类型转换:
    int类型到long、float或者double类型
    long类型到float、double类型
    float类型到double类型
    窄化类型转换:
    i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l和d2f
  5. 对象创建于访问指令
    创建类实例指令:new
    创建数组指令:newarray、anewarray、multianewarray
    访问类字段指令:getfield、putfield、getstatic、putstatic
    把一个数组元素加载到操作数栈的指令:baload、caload、saload、iaload、laload、faload、daload、aaload
    将一个操作数栈的值存储到数组元素中的指令:bastore、castore、sastore、iastore、fastore、dastore、aastore
    取数组长度的指令:arraylength
    检查类实例类型的指令:instanceof、checkcast
  6. 操作数栈管理指令
    将操作数栈的栈顶一个或两个元素出栈:pop、pop2
    复制栈顶一个或两个数值并将复制值或双份的复制值重新压入栈顶:dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2
    将栈最顶端的两个数值交换:swap
  7. 控制转移指令
    条件分支:ifeq、iflt、ifle、ifne、ifgt、ifge、ifnull、ifnonnull、if_icmpeq、if_icmpne、if_icmplt、if_icmpgt、if_icmple、if_icmpge、if_acmpeq和if_acmpne
    复合条件分支:tableswitch、lookupswitch
    无条件分支:goto、goto_w、jsr、jsr_w、ret
  8. 方法调用和返回指令
    invokevirtual指令用于调用对象的实例方法
    invokeinterface指令用于调用接口方法
    invokespecial指令用于调用一些需要特殊处理的实例方法
    invokestatic指令用于调用类方法
    invokedynamic指令用于在运动时动态解析出调用点限定符所引用的方法
    注:前四条调用指令的分派逻辑都固化在Java虚拟机内部,而invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。
  9. 异常处理指令
    显式抛出异常指令athrow
  10. 同步指令
    Java虚拟机支持方法级同步和方法内部一段指令序列的同步,都是通过管程(Monitor)来支持的。
    指令:monitorenter、monitorexit
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容