class文件结构

183 阅读4分钟

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

类文件结构

java源代码经过javac编译后生成.class文件,在经过jvm即可执行代码。 类文件结构中有两种数据类型:

  • 无符号数: 如u1,u2,u3,u4、u8分别标示一个字节、两个字节、三个字节、四个字节、八个字节。无符号数可以表示数字、以及编码后的字符串
  • 表: 类似结构体的结构,由多个表后者无符号数构成。

.class文件结构

image.png

下面编写一个类文件查看

package com.sanjin.jvm;


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author: jiangxch
 * @date: 2021/4/11 上午4:30
 */
public class NewObjectTest {
    public static void main(String[] args) throws InterruptedException {
        Object object = new NewObjectTest();
    }
}

编译为.class文件后,通过vscode查看十六进制。

image.png

按照class文件数据结构逐个分析:

magic u4

CA FE BA BE

四字节cafebabe魔数,用来判断文件是否是一个class文件,如果jpg,png都有自己的魔数。当然这个数字也能够伪造。

minor_version/mgjor_version

2字节副版本号+2字节主版本号,版本号用来标识当前.class可以被哪些版本的jvm解析,高版本的class文件无法被低版本的jvm解析。

minor_version=00 00=0

major_version=00 34=52(使用jdk8编译的)

constant_pool_count

constant_pool_count=00 13=19

constant_pool

常量池,常量池在.class占据了很大一部分空间,主要存储两种类型常亮:

  • 字面量: 即Java中的常亮,如字符串,标示为final的常量值等
  • 符号引用: 当通过import引入其它类,调用其它类的方法时,因为此时不不知道这些方法的跳转地址,所以在编译时使用符号引用代替,比如调用方法就使用方法签名标示,在JVM加载.class文件时,会从常量池获得对应的符号引用,再在类创建时或运行时解析、找到其真正的内存地址。

主要包含下面几种符号引用:

  1. 被模块导出或者开放的包,如package com.xxx.jjj
  2. 类和接口的全限定名,如com.sanjin.HelloWorld
  3. 字段的名称和描述符,如private int a
  4. 方法的名称和描述符,如private static void sayHelo(int a)
  5. 方法句柄和方法类型
  6. 动态调用点和动态常亮

constant_pool结构为cp_info表,共有19-1=18个cp_info表,每个cp_info表结构起始第一个字节是标志位,根据标志位的不同,每个常量池又代表不同的含义。

image.png

image.png

  • Utf8_info: 字符串,如 "NewObjectTest.java"
  • Integer_info: final static 修饰的整数。
  • Class_info: 类的符号引用

access_flags

常量池之后即为类的访问标志

image.png

具备多个访问标志,进行或操作即可。

this_class/super_class/interfaces_count/interfaces

这三个东西用来确定类的继承关系。 this_class和super_class指向CONSTANT_Class_info常量池索引。用javap -v XXX.class编译后可以看到CONSTANT_Class_info即为Class的符号引用。

image.png

因为Java支持多实现,所以接口有数量定义。interfaces存储的也是CONSTANT_Class_info索引。

fields_count/fields

字段表。fileds_count标示字段数量。fields类型为表 field_info,其结构如下:

image.png

access_flags类型为:

image.png

字段的定义:

image.png

I标示int类型: 注意这几种类型不同:

image.png

method_count/methods

方法表示。 methods数据结构为表:method_info:

image.png

access_flags的类型有:

image.png

name_index标示方法的简单名称。

descriptor_index表示方法的入参以及返回值类型:

image.png

括号中为入参类型,括号右侧为返回值类型。

除了方法的定义,还有方法代码的定义,存放在随后的artribute_info属性表中,其表结构如下:

image.png

字段、方法、类可能都会有属性表。比如字段后面可能会跟着ConstantValue(当定义final 常亮就会存在)。方法后面则是方法对应的虚拟机字节码。

方法表对应的 attribute_name 是 "Code",Code表的结构为:

image.png 其中code即为虚拟机的字节码,也是JVM执行的代码。

总结

class文件结构非常枯燥,这篇也写的非常水,之前看书时跳过去了,但跟着书自己动手编译、翻译实践一遍,会收货很多,尤其是它协议的定制,是不是与socket协议的定义有着异曲同工之妙。