Class File Format

The structure of a class file is shown in the table below:

Byte OffsetByte SizeType/ValueDescription
040xCAFEBABEmagic number
42unsigned 16-bit intminor version
62unsigned 16-bit intmajor version
82 (cplen)unsigned 16-bit intconstant count
10cpsizelistconstant pool (index starts from 1)
10+cpsize2unsigned 16-bit intaccess flags
12+cpsize2unsigned 16-bit intindex of current Class in constant pool
14+cpsize2unsigned 16-bit intindex of parent Class in constant pool
16+cpsize2 (ilen)unsigned 16-bit intnumber of interfaces implemented by current Class
18+cpsizeilen * 2unsigned 16-bit int[]indices of interfaces implemented by current Class in constant pool
18+cpsize+isize2unsigned 16-bit intfield count
20+cpsize+isizefsizelistfield list
20+cpsize+isize+fsize2unsigned 16-bit intmethod count
22+cpsize+isize+fsizemsizelistmethod list
22+cpsize+isize+fsize+msize2unsigned 16-bit intattribute count
24+cpsize+isize+fsize+msizeasizelistattribute list

For more detailed class file format, please refer to: Java Virtual Machine Specification: Chapter 4. The class File Formatopen in new window.

class Version

As an Android or Java developer, most people only care about the javac version number. So why do class files also have version numbers? -- It's for the JRE to check. Here are two examples:

  1. Java 7 executing a class compiled with Java 8 that contains lambda expressions
  2. Java 7 executing a class compiled with Java 8 that does not use any Java 8 new features

Under normal circumstances, neither of the above scenarios will execute successfully. Why? Because Java 7 does not support class files compiled with Java 8. But someone might ask: in the second scenario, I didn't use any Java 8 new features, so why can't Java 7 execute it? -- Because of the major version. Lower version JREs do not support running higher version class files.

How can we make the second scenario work properly? -- When compiling with Java 8, specify -target 1.7 or lower. In Gradle, this would be:

java {
    targetCompatibility = JavaVersion.VERSION_1_7
}
1
2
3

The major version for each Java version is shown in the table below:

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

Constant Pool

Every class file has a constant pool. Besides Integer, Float, Long, Double, and String, it also contains Class, Field references, Method references, and more. Therefore, the constant pool occupies a large portion of the entire class file. This is one of the reasons why Android uses the dex format -- to solve the space waste caused by constant pools. Of course, this is only one of the reasons.

Attributes

From the structure of the class file, we can see that whether it's class, field, or method, attribute is always located at the end of its structure. Why isn't it placed at the beginning? The main reason is that the JVM specification supports compilers defining their own specific attributes (for example: Sootopen in new window supports adding new attributes to class filesopen in new window). It's precisely this extensibility of attributes that makes them the most complex part of the class file structure.

Among the 23 attributes defined in the JVM specification, the following 6 are optional for the JVM:

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

This provides developers who are optimizing application size with an approach to consider.