class 文件格式

class 文件的结构如下表所示:

字节偏移量字节数类型/值描述
040xCAFEBABEmagic number
42unsigned 16-bit intminor version
62unsigned 16-bit intmajor version
82 (cplen)unsigned 16-bit int常量数
10cpsizelist常量池(索引从 1 开始)
10+cpsize2unsigned 16-bit intaccess flags
12+cpsize2unsigned 16-bit int当前 Class 在常量池中的索引
14+cpsize2unsigned 16-bit int父类 Class 在常量池中的索引
16+cpsize2 (ilen)unsigned 16-bit int当前 Class 实现的接口数
18+cpsizeilen * 2unsigned 16-bit int[]当前 Class 实现的接口在常量池中的索引
18+cpsize+isize2unsigned 16-bit int字段数
20+cpsize+isizefsizelist字段列表
20+cpsize+isize+fsize2unsigned 16-bit int方法数
22+cpsize+isize+fsizemsizelist方法列表
22+cpsize+isize+fsize+msize2unsigned 16-bit int属性数
24+cpsize+isize+fsize+msizeasizelist属性列表

更详细的 class 文件格式,请参考:Java Virtual Machine Specification: Chapter 4. The class File Format在新窗口打开

class 版本

作为 Android 或者 Java 开发者,大多都只关心 javac 的版本号,为什么 class 文件也有版本号呢?—— 是给 JRE 看的,举两个例子:

  1. Java 7 执行用带有 Java 8 编译的带 lambdaclass
  2. Java 7 执行用 Java 8 编译的不带 Java 8 新特性的 class

正常情况下,以上两种情况都不会执行成功,为什么?因为 Java 7 不支持 Java 8 编译的 class ,可是有人会问:第 2 种情况下,我并没有用到 Java 8 的新特性,为什么 Java 7 就不能执行呢?—— 因为 major version 的原因,低版本的 JRE 是不支持运行高版本的 class 的。

如何让第 2 种情况也能正常运行呢?—— 在用 Java 8 编译时指定 -target 1.7 或者更低即可,在 Gradle 中则是:

java {
    targetCompatibility = JavaVersion.VERSION_1_7
}
1
2
3

Java 各版本的 major version 如下表所示:

Java Versionmajor version
Java SE 1458
Java SE 1357
Java SE 1256
Java SE 1155
Java SE 1054
Java SE 953
Java SE 852
Java SE 751
Java SE 650
Java SE 549
JDK 1.448
JDK 1.347
JDK 1.246
JDK 1.145

常量池

每个 class 文件中都有一个常量池,除了 IntegerFloatLongDoubleString 以外,还有 ClassField 引用,Method 引用等等,所以,常量池占了整个 class 文件很大一部分空间,这也是为什么 Android 采用 dex 这种格式,来解决常量池带来空间浪费,当然,这只是其中原因之一。

属性

class 文件的结构,我们可以看出,无论是 class 还是 field ,抑或是 methodattribute 都是位于其结构的最末尾,为什么没放在前面的位置呢?主要是因为 JVM 规范支持编译器定义自己特有的 attribute 的(例如:Soot在新窗口打开 就支持class 文件新增 attribute在新窗口打开) ,正是 attribute 的可扩展性,这也使得 attribute 成为 class 文件结构中最复杂的部分。

在 JVM 规范定义的 23 种 attribute 中,以下 6 种对于 JVM 来说是可选的:

  • SourceFile
  • SourceDebugExtension
  • LineNumberTable
  • LocalVariableTable
  • LocalVariableTypeTable
  • Deprecated

所以,这给优化应用 size 的开发者提供了一个思路。